<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>songha-5.log</title>
        <link>https://velog.io/</link>
        <description>기본이 탄탄한 FE가 되고싶습니다</description>
        <lastBuildDate>Tue, 01 Sep 2026 08:30:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>songha-5.log</title>
            <url>https://velog.velcdn.com/images/songha-5/profile/24a2939d-bb46-4a73-b4ab-bbef622e64a2/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. songha-5.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/songha-5" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[포트폴리오 작업 기록]]></title>
            <link>https://velog.io/@songha-5/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%9E%91%EC%97%85-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@songha-5/%ED%8F%AC%ED%8A%B8%ED%8F%B4%EB%A6%AC%EC%98%A4-%EC%9E%91%EC%97%85-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Tue, 01 Sep 2026 08:30:16 GMT</pubDate>
            <description><![CDATA[<h3 id="작업배경">작업배경</h3>
<p>기존에 작업하던 프로젝트를 하나 끝낸 후, 이력서를 작성하려고하니. 웹사이트 이미지나, 웹사이트 링크를 첨부하기 어렵고, 내가 어떤사람인지 한눈에 보여주기 힘들다는 문제를 겪었다. 그래서 새 미니 프로젝트로 포트폴리오 웹사이트를 만들어, 이때 까지 이력 및 작업들을 정리하여 제출할 예정이다.</p>
<h3 id="포트폴리오-구성">포트폴리오 구성</h3>
<p>포트폴리오를 어떻게 구성할지 정리가 필요했다.
구성은 다른 포트폴리오와 크게 다를바없이 무난한 구조로하여 사용자가 익숙히 접할 수 있도록 할 예정이다.</p>
<ul>
<li>메인 (내정보)</li>
<li>나에 대해서 </li>
<li>스킬</li>
<li>이력</li>
<li>프로젝트</li>
<li>연락처</li>
</ul>
<p>이렇게 6단계로 구성할 예정이며. 원스크롤로 한눈에 볼 수 있도록 구성할 예정이다.</p>
<p><strong>참고한 포트폴리오</strong>
<a href="https://ssongmi.github.io/">https://ssongmi.github.io/</a>
<a href="https://portfolio.project-hh.com/">https://portfolio.project-hh.com/</a>
<a href="https://min-hyuk.com/">https://min-hyuk.com/</a>
<a href="https://www.meganmagic.com/ko">https://www.meganmagic.com/ko</a>
<a href="https://portfolio.biud436.com/">https://portfolio.biud436.com/</a></p>
<h2 id="디자인">디자인</h2>
<p>디자인은 핀터레스트에서 1차 수집을했고, 그 다음엔 ai를 이용하여 제작하고자 했다.</p>
<p>가장 큰 타이포와 여백으로 구성하고자 했다. 레트로나 키치등 다양한 표현 방법이 있었으나, 깔끔하게 만들어 보다 큰 신뢰를 주는 방향으로 결정하였다.</p>
<p>그리고 찾은 레퍼런스들을 취합해 ai의 도움을 받아 기본 디자인틀을 완성하였다</p>
<p><strong>사용한 ai 리스트</strong>
<a href="https://bolt.new">https://bolt.new</a>
<a href="https://stitch.withgoogle.com/">https://stitch.withgoogle.com/</a></p>
<h2 id="바이브-코딩-사용-여부">바이브 코딩 사용 여부</h2>
<p>작성중입니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[My Garden 트러블슈팅]]></title>
            <link>https://velog.io/@songha-5/My-Garden-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@songha-5/My-Garden-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</guid>
            <pubDate>Thu, 23 Jul 2026 04:15:53 GMT</pubDate>
            <description><![CDATA[<h1 id="트러블-슈팅">트러블 슈팅</h1>
<p>My Garden프로젝트를 만들었을때 일어났던 트러블슈팅리스트를 작성하고자 합니다.
My Garden은 식물의 급수 주기, 식물 성장 기록 다이어리 기능을 가지고 있는 웹사이트입니다. </p>
<h1 id="modal-animation---fade-in-fade-out">Modal Animation - fade-in, fade-out</h1>
<h2 id="문제-원인">문제 원인</h2>
<p>모달을 열고 닫을때, 모달이 아래에서 위로, 오퍼시티를 적용해서 부드럽게 애니메이션이 적용되게하려 하였지만 애니메이션이 팝업 실행시 한번만 적용되고 팝업 닫기, 팝업 재오픈때 애니메이션이 적용이 안되는 문제가 발생했습니다.</p>
<blockquote>
<p>모달을 닫을때 뒤의 <strong>백그라운드는 늦게 사라지고(애니메이션 적용O) 안의 컨텐츠박스가 바로 사라져(애니메이션 적용X)</strong> 애니메이션이 적용 안되는것처럼 보이는 문제</p>
</blockquote>
<h2 id="문제해결">문제해결</h2>
<p><strong>1. 컨텐츠박스의 애니메이션이 적용 안되는 문제</strong>
<strong>2. setTimeout적용 문제</strong>
<strong>3. 애니메이션 시간</strong></p>
<h3 id="컨텐츠박스의-애니메이션이-적용-안되는-문제">컨텐츠박스의 애니메이션이 적용 안되는 문제</h3>
<p>문제를 더 파해쳐보니 애니메이션은 적용이 되어있었습니다. 다만 모달메인컨텐츠가 모달을 닫을때 null이 되어 내용이 보이지 않고 사라져서 애니메이션이 적용이 안된것으로 보여졌습니다.
그래서 useState를 활용해 <strong>컨텐츠를 다른곳에 보관</strong>하여, 해당 컨텐츠를 애니메이션이 닫힐때까지 쭉 보여질 수 있도록 적용하였습니다.</p>
<pre><code class="language-jsx"> const { isOpen, content, closeModal } = useModalstore()
 const [ displayContent, setDisplayContent ] = useState(content)

 ...

 &lt;div className=&quot;shadow-lg rounded-2xl bg-white&quot;&gt;
   {displayContent}
 &lt;/div&gt;</code></pre>
<p>기존은 <code>content</code>를 그대로 넣어, 바로 null이되어 컨텐츠가 보이지 않았습니다. 그래서 useState에 <code>content</code>를 넣어 해당 값을 보관하였습니다. 그리고 조건식(모달이 열렸을때, 컨텐츠가 있을때, 컨텐츠가 기존컨텐츠와 같지 않을때)을 넣어, 컨텐츠가 null이면서 모달이 열렸을때 컨텐츠를 넣어 표기하도록 하였습니다.
만든 <code>useState</code>를 JSX에 넣어 본래의 <code>content</code>가 null이 되어도, 해당 값은 <code>useState</code>에 있는 컨텐츠를 넣기때문에 화면이 사라지지 않고 그대로 표현할 수 있게 되었습니다. </p>
<h2 id="settimeout적용-문제">setTimeout적용 문제</h2>
<p>기존에는 <code>setTimeout</code>으로 애니메이션 시간을 적용했습니다. 하지만 시간을 바꾸려면 <code>tailwind</code>와 <code>setTimeout</code>두 군대를 바꿔야했고, 씹힘문제나 안정성이 떨어져서 <code>requestAnimationFrame</code>으로 변경하게 되었습니다.
<code>requestAnimationFrame</code>은 화면 렌더가 다 끝난 후 적용이되기때문에 애니메이션에 적절하였으며, 모달이 채 실행되기전에 닫거나 했을때 일어날 문제를 차단하여 안정성을 챙겼습니다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
    if (!modalRef.current) return

    let raf1: number, raf2: number

    if (isOpen) {
      modalRef.current.showModal()
      document.body.style.overflow = &quot;hidden&quot;

      raf1 = requestAnimationFrame(() =&gt; {
        raf2 = requestAnimationFrame(() =&gt; setIsAnimating(true))
      })
    } else {
      raf1 = requestAnimationFrame(() =&gt; setIsAnimating(false))
    }

    return () =&gt; {
      cancelAnimationFrame(raf1)
      cancelAnimationFrame(raf2)
    }
  }, [isOpen])</code></pre>
<h3 id="애니메이션-시간">애니메이션 시간</h3>
<p><code>setTimeout</code>을 활용해서 시간설정을하면 <code>tailwind</code>와 <code>setTimeout</code>에 시간설정을 해줘야합니다. 유지보수에 힘들고, 실수가 일어나기 쉬운 환경입니다. 그래서 <code>addEventListener</code>를 통해 시간관리를 하고자 했습니다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  const node = modalRef.current
  if (!node) return

  const handleTransitionEnd = (e: TransitionEvent) =&gt; {
    if (e.target !== node) return
    if (!isOpen) {
      node.close()
      document.body.style.overflow = &quot;unset&quot;
    }
  }

  node.addEventListener(&quot;transitionend&quot;, handleTransitionEnd)
  return () =&gt; node.removeEventListener(&quot;transitionend&quot;, handleTransitionEnd)
}, [isOpen])</code></pre>
<p><code>addEventListener</code>의 &quot;transitioned&quot;를 통해 애니메이션이 끝나면 모달 닫는 액션이나옵니다. 그러면 <code>setTimeout</code>에서 시간조절을 하지않아도, <code>tailwind</code>에 적용되어있는 시간에 맞춰 애니메이션 후 팝업닫힘이 실행됩니다.</p>
<h2 id="느낀점배운점">느낀점/배운점</h2>
<p>처음에는 모달에 애니메이션을 추가하는것을 쉽게 생각했습니다. opacity나 translate만 신경쓰면 된다고 생각했는데, 팝업이 닫혀야하고 동시에 컨텐츠도 애니메이션이 끝날때까지 보여야한다는점, 모달을 여러번 여닫을때 에러가 나지않도록 함수 실행시점을 고려해야한다는점들을 만나 여러모로 배울 수 있었습니다. 특히 개발자 관점으로 기능을 생각해야한다는점을 배울 수 있었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터5 - 함수컴포넌트/클래스컴포넌트, Props Derilling]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B05-%ED%95%A8%EC%88%98%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%ED%81%B4%EB%9E%98%EC%8A%A4%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Props-Derilling</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B05-%ED%95%A8%EC%88%98%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%ED%81%B4%EB%9E%98%EC%8A%A4%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Props-Derilling</guid>
            <pubDate>Wed, 24 Jun 2026 01:54:58 GMT</pubDate>
            <description><![CDATA[<h1 id="함수-컴포넌트-vs-클래스-컴포넌트">함수 컴포넌트 vs 클래스 컴포넌트</h1>
<h2 id="함수-컴포넌트와-클래스-컴포넌트의-특징">함수 컴포넌트와 클래스 컴포넌트의 특징</h2>
<p>구분|함수형 컴포넌트|클래스 컴포넌트|
|--|--|--|
|장점|코드가 훨씬 간결하고 가독성이 높음, custom hook을 통한 로직 재사용성이 뛰어남 | 객체 지향 프로그래밍(OOP)에 익숙한 개발자에게 친숙함, <strong><em>Error Boundary 구현때 필수</em></strong>로 사용
|단점|Hooks등 의존성배열 관리나 클로저 동작 원리에 대한 추가 학습이 필요| 코드가 길고 장황해지기 쉬움, <code>this</code>키워드 바인딩 문제로 인한 혼란이 잦음, 상태나 라이프사이클 로직을 재사용하기 복잡함
| 상태 및 생명주기| React hooks을 사용해 관리 | this.state와 미리 정의된 라이프사이클 매서드를 사용하여 관리|</p>
<h2 id="error-boundary">Error Boundary</h2>
<p>Error Boundary는 <strong>반드시 클래스 컴포넌트</strong>로 적용해야합니다.</p>
<blockquote>
<p>error boundary란? 오류 발생 시 앱 전체가 중단되지않고 에러난 해당 컴포넌트만 격리하여 앱 전체가 죽지 않게 하는것</p>
</blockquote>
<p>Error Boundary는 componentDidCatch와 getDerivedStateFromError 같은 클래스 컴포넌트 전용 생명주기 메서드에 의존하기 때문입니다.</p>
<pre><code class="language-tsx">import React from &#39;react&#39;;

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  // 에러가 발생했을 때 호출되어 상태를 업데이트
  static getDerivedStateFromError(error) {
    // 다음 렌더링에서 폴백 UI가 보이도록 상태를 변경
    return { hasError: true };
  }

  // 에러에 대한 부가 정보를 기록
  componentDidCatch(error, errorInfo) {
    // 에러 로깅 서비스로 데이터를 전송
    console.error(&quot;에러가 포착되었습니다:&quot;, error, errorInfo)
  }

  render() {
    // 에러가 발생했다면 준비해둔 대체 UI를 렌더링
    if (this.state.hasError) {
      return (
        &lt;div&gt;
          &lt;h2&gt;문제가 발생했습니다.&lt;/h2&gt;
          &lt;p&gt;화면을 불러오는 중 오류가 생겼습니다. 새로고침 해주세요.&lt;/p&gt;
        &lt;/div&gt;
      )
    }

    // 에러가 없다면 자식 컴포넌트를 정상적으로 렌더링
    return this.props.children
  }
}

export default ErrorBoundary</code></pre>
<h2 id="props-drilling">Props Drilling</h2>
<p>Props Drilling이란 부모요소에서 Props A, B를 사용하고, 이 Props를 손자도 사용해야한다고 가정합시다.</p>
<p>그럼 부모에서 props A, B를 만들고 자식 &gt; 손자까지 내려주어야합니다. </p>
<p>지금은 단순히 3단계지만 이 단계가 깊어지면 어떨까요?</p>
<p>이런것을 해결하기 위해 <code>Context API</code>가 등장했습니다</p>
<h3 id="context-api로-props-drilling-해결">Context API로 Props Drilling 해결</h3>
<p>Context API는 Props Drilling을 해결하기 위해 나왔습니다.
위의 경우처럼 해당 Props를 여러곳에서 사용해야한다면 Context API를 사용하는게 보다 효율적으로 관리하기 좋습니다.</p>
<pre><code class="language-tsx">import React, { createContext, useContext, useState } from &#39;react&#39;;

// Context 생성 (초기값은 비워두거나 기본값 설정 가능)
const ThemeContext = createContext(null)

// ==========================================

export default function App() {
  // 전역으로 관리할 테마 상태
  const [theme, setTheme] = useState(&#39;light&#39;)

  // 테마를 토글하는 함수
  const toggleTheme = () =&gt; {
    setTheme((prevTheme) =&gt; (prevTheme === &#39;light&#39; ? &#39;dark&#39; : &#39;light&#39;));
  };

  return (
    // Provider로 하위 컴포넌트들을 감싸고, 공유할 데이터(value)를 넣습니다.
    &lt;ThemeContext.Provider value={{ theme, toggleTheme }}&gt;
      &lt;div style={{ padding: &#39;20px&#39;, border: &#39;2px solid blue&#39; }}&gt;
        &lt;h2&gt;최상단 부모 컴포넌트 (App)&lt;/h2&gt;
        &lt;MiddleComponent /&gt;
      &lt;/div&gt;
    &lt;/ThemeContext.Provider&gt;
  );
}

// ==========================================

// (중간 컴포넌트) - props를 전혀 받지 않고 자식에게 넘겨주지도 않습니다
function MiddleComponent() {
  return (
    &lt;div style={{ padding: &#39;20px&#39;, border: &#39;2px solid green&#39;, margin: &#39;10px 0&#39; }}&gt;
      &lt;h3&gt;중간 컴포넌트&lt;/h3&gt;
      &lt;p&gt;여기서는 theme 데이터를 몰라도 됩니다.&lt;/p&gt;
      &lt;DeepChildComponent /&gt;
    &lt;/div&gt;
  );
}

// ==========================================

// 상태를 실제 사용하는 깊은 곳의 자식 컴포넌트
function DeepChildComponent() {
  // useContext 훅을 사용해 ThemeContext의 value를 바로 꺼내옵니다.
  const { theme, toggleTheme } = useContext(ThemeContext)

  const style = {
    background: theme === &#39;light&#39; ? &#39;#ffffff&#39; : &#39;#333333&#39;,
  }

  return (
    &lt;div style={style}&gt;
      &lt;h4&gt;깊은 자식 컴포넌트&lt;/h4&gt;
      &lt;p&gt;현재 테마: &lt;strong&gt;{theme}&lt;/strong&gt;&lt;/p&gt;
      &lt;button onClick={toggleTheme}&gt;테마 바꾸기&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>위의 코드를 보시면 테마에 필요한것은 provider로 감싸서 설정 후, 아래 손자에서 context를 불러와 적용하는 모습을 볼 수 있습니다. 이렇게하면 불필요한 props 전달이 사라지고 보다 깔끔하고, 관리하기 쉽습니다.</p>
<p><strong>주의사항</strong>
위에 테마 코드를 보시면 부모 컴포넌트에서 provider로 감싸여 진것을 알 수 있습니다.
provider에 감싸여진 자식들의 컴포넌트에서만 사용할 수 있고 감싸지지 않은곳은 사용할 수 없다는점을 알아두어야합니다.</p>
<blockquote>
<p>Context API의 무분별한 사용을 자제해야합니다.
<strong>전체 코드에서 사용할 코드</strong>(다크테마, 로그인상태, 다국어 등)처럼 코드 변경이 자주되지도 않고, 전역에서 관리해야하는 코드들만 사용하는것을 추천합니다.
단순히 Props전달을 위한 Context API를 만들어 <strong>남용</strong>한다면 <strong>성능과 상태추적이 힘들어집니다.</strong></p>
</blockquote>
<p><strong>라이브러리</strong>
상태관리 라이브러리들이 있습니다. <strong>Recoil, Zustand, Jotai, Redux</strong>등이 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터4 - JSX]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-</guid>
            <pubDate>Fri, 19 Jun 2026 02:42:29 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-jsx">✨ JSX</h1>
<p>가장 기본적인 구성 요소입니다.
자바스크립트에 HTML문법을 결합한 형태로 구조를 함수처럼 작성할 수 있게 합니다.</p>
<blockquote>
<p><strong>JSX의 도입</strong>
예전에는 HTML, CSS, JS 다 분리하여 작업했습니다. 그러다보니 파일 관리나 유지보수에 힘들었습니다. JSX를 통해 HTML,CSS,JS를 통합하여 관리할 수 있게 되었습니다.</p>
</blockquote>
<pre><code class="language-jsx">// JS (로직): 상태(State) 관리
import { useState } from &#39;react&#39;

export default function SimpleButton() {
  const [isActive, setIsActive] = useState(false)

  // CSS (스타일): JS 변수에 따라 스타일이 변함
  const buttonStyle = {
    backgroundColor: isActive ? &#39;hotpink&#39; : &#39;gray&#39;,
    color: &#39;white&#39;,
    padding: &#39;10px 20px&#39;,
    border: &#39;none&#39;,
    cursor: &#39;pointer&#39;
  }

  //HTML (구조): JSX 문법
  return (
    &lt;button style={buttonStyle} onClick={() =&gt; setIsActive(!isActive)}&gt;
      {isActive ? &#39;활성화됨&#39; : &#39;클릭하세요&#39;}
    &lt;/button&gt;
  );
}</code></pre>
<p><strong>JSX 장점</strong></p>
<ul>
<li>관련 코드가 가까이있어 이해하기 쉬움</li>
<li>컴포넌트 단위로 작업가능하여 유지보수가 쉽고 재사용성이 높음</li>
<li>파일분리가아닌 관심사분리 중심</li>
</ul>
<h2 id="jsx의-주요-특징">JSX의 주요 특징</h2>
<h3 id="html과-유사한-문법">HTML과 유사한 문법</h3>
<p>HTML문법과 유사합니다. 그래서 배움에도 쉽고 개발자에게 친숙하여 접근하기 좋습니다.</p>
<h3 id="자바스크립트-표현식-사용-가능">자바스크립트 표현식 사용 가능</h3>
<pre><code class="language-jsx">&lt;button style={buttonStyle} onClick={() =&gt; setIsActive(!isActive)}&gt;
  {isActive ? &#39;활성화됨&#39; : &#39;클릭하세요&#39;}
&lt;/button&gt;</code></pre>
<p>JSX에 자바스크립트를 넣어 분기를 만들 수 있습니다. 이를통해 UI나 연산결과등 다양하게 활용 할 수 있습니다.</p>
<h3 id="하나의-루트-요소만-반환">하나의 루트 요소만 반환</h3>
<pre><code class="language-jsx">return &lt;p&gt;두개의 요소&lt;/p&gt;&lt;p&gt;반환&lt;/p&gt;  // 에러
return &lt;&gt;&lt;p&gt;두개의 요소&lt;/p&gt;&lt;p&gt;반환&lt;/p&gt;&lt;/&gt; // 성공</code></pre>
<p>JSX는 반드시 하나의 요소만 반환해야합니다.</p>
<h3 id="스타일링-방식의-차이">스타일링 방식의 차이</h3>
<p>html의 class는 className으로 변경하여 사용합니다.
그리고 인라인일때는 객체형식으로 작성해야합니다.</p>
<pre><code class="language-jsx">&lt;p className=&quot;content&quot;&gt;..&lt;/p&gt;
&lt;div className={{color: red, fontSize: &quot;12px&quot; }}&gt;..&lt;/div&gt;</code></pre>
<h2 id="jsx의-호환성과-발전">JSX의 호환성과 발전</h2>
<p>예전에는 JSX를 변환하기위해서 vabel등을 활용해 변환해야했습니다. 파일 확장자도 .jsx, .tsx가 사용되어야했습니다.
하지만 기술이 발전되면서 다양한 트랜스파일러들이 등장했고(SWC, esbuild 등) JSX문법을 자동으로 인식하여 처리할 수 있게 되었습니다.
이제는 ts파일 내에서도 jsx문법을 사용할 수 있게 가능해졌습니다.</p>
<h2 id="컴포넌트가-dom에-보이기까지">컴포넌트가 DOM에 보이기까지</h2>
<p><strong>1. JSX 트랜스 파일 (Build Time)</strong></p>
<ul>
<li><p>작성한 JSX 코드는 브라우저가 직접 이해할 수 없습니다.</p>
</li>
<li><p>Babel과 같은 도구가 JSX를 <code>React.createElement()</code>호출문으로 변환합니다.</p>
<pre><code class="language-jsx">// 작성코드
const element = &lt;h1 className=&quot;title&quot;&gt;Hello, React!&lt;/h1&gt;
// 변환
const element = React.createElement(
&#39;h1&#39;, 
{ className: &#39;title&#39; }, 
&#39;Hello, React!&#39;
)</code></pre>
</li>
<li><p><em>2. 렌더링(Render Phase)*</em></p>
</li>
<li><p>React는 컴포넌트를 호출하여 Virtual DOM을 생성합니다.</p>
</li>
<li><p>이전과 이후의 Virtual DOM을 비교하여 실제로 변경된 부분만 골라냅니다 (Diffing 알고리즘)</p>
</li>
</ul>
<p><strong>3. 커밋(Commit Phase)</strong></p>
<ul>
<li>변경된 부분(Diff)만 추출하여 실제 DOM에 반영합니다.</li>
<li>표준 HTML로 변환됩니다.</li>
</ul>
<p><strong>4. 브라우저 페인팅(Browser Painting)</strong></p>
<ul>
<li>업데이트된 DOM을 바탕으로 스타일을 계산하고 레이아웃을 잡아 픽셀을 그립니다.</li>
</ul>
<h2 id="자바스크립트-코드-변환도구">자바스크립트 코드 변환도구</h2>
<p>JSX나 최신 자바스크립트 문법을 사용할 수 있는건 <strong>컴파일러 또는 트랜스파일러</strong>덕분입니다.</p>
<p><strong>컴파일러</strong></p>
<ul>
<li>고수준언어(Java, C++)을 저수준 언어(기계어, 바이트코드)로 변환하는 도구입니다.</li>
</ul>
<p><strong>트랜스파일러</strong></p>
<ul>
<li><p>고수준 언어를 동일 수준의 다른언어, 또는 같은 언어의 다른버전으로 변환하는 도구 (Babel - ES6+ &gt; ES5)  소스-투-소스(source-to-source)</p>
</li>
<li><p><strong>바벨 (Babel)</strong>: Babel은 AST(Absract Syntax Tree)를 생성하고 이를 바탕으로 코드를 분석하고 재구성합니다. 이는 컴파일러와 매우 유사한 작동방식을 가지고있습니다.</p>
</li>
<li><p><strong>비트(Vite)</strong>: 매우 빠른 모듈 핫 리로딩과 빌드 속도 최적화로 알려져 있습니다. Babel을 사용하지 않고 esbuild나 SWC를 활용합니다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터4 - 프레임워크, 컴포넌트, State와 Props]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B04-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B04-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Fri, 19 Jun 2026 01:34:55 GMT</pubDate>
            <description><![CDATA[<h1 id="👀-리액트-프레임워크와-핫모듈hmr">👀 리액트 프레임워크와 핫모듈(HMR)</h1>
<h2 id="프레임워크를-사용하는-이유">프레임워크를 사용하는 이유</h2>
<p>리액트는 <strong>UI라이브러리</strong>입니다. 그래서 사용자가 직접 라우팅, 데이터패칭, 상태관리는 <strong>개발자가 직접 설정</strong>해야 합니다. 하지만 앱이 커지고 복잡해지면 여러 문제가 발생하곤 합니다.</p>
<h3 id="중구난방인-라우팅과-데이터-페칭">중구난방인 라우팅과 데이터 페칭</h3>
<p>페이지가 늘어나면서 라우터 설정과 코드 분할, 데이터 로딩 로직이 중구난방 되기 쉽상입니다. 게다가 라이브러리끼리의 상성도 생각하여 조합해야합니다.</p>
<h3 id="클라이언트-중심-처리의-한계">클라이언트 중심 처리의 한계</h3>
<p>모든 데이터를 클라이언트에서 불러오면 네트워크 지연이 발생하고 초기 렌더링속도가 저하될 수 있습니다.</p>
<h3 id="복잡한-설정으로-인한-유지보수-어려움">복잡한 설정으로 인한 유지보수 어려움</h3>
<p>직접 설정한 번들러, 라우터 API 클라이언트 등은 규모가 커질수록 유지보수가 어렵습니다. 게다가 SSR(서버사이드렌더링), SSR(정적 사이트 생성)등의 기능을 추가하려면 더욱 복잡한 설명을 감수해야합니다.</p>
<h2 id="hot-module-replacementhmr">Hot Module Replacement(HMR)</h2>
<p>코드가 변경될때 화면 전체가 변경되지않고 브라우저가 자동으로 변경된 내용만 반영하는것을 말합니다. 이 과정은 빠르게 진행되며, UI가 깜박이거나 앱이 초기화되지 않기때문에 개발 효율성을 크게 높힙니다.</p>
<p>다만 HMR이 항상 완벽하게 동작하는것은 아닙니다. 전역 상태가 복잡하게 연결되어있는 경우나 라우팅 변경이 포함된 구조에는 HMR이 예상대로 작동하지 않습니다.</p>
<h1 id="💎-컴포넌트">💎 컴포넌트</h1>
<p>리액트의 가장 작은단위는 컴포넌트 입니다. 컴포넌트들이 모여 페이지를 이루어냅니다. 컴포넌트에서 쓰는 확장문법은 JSX입니다. HTML처럼 생긴 코드입니다.</p>
<pre><code class="language-jsx">function App () {
  return &lt;h1&gt;테스트&lt;/h1&gt;
}
export default App</code></pre>
<h2 id="여러-컴포넌트-조합하기">여러 컴포넌트 조합하기</h2>
<p>컴포넌트 끼리 조합을 하여 사용할 수 있습니다. 컴포넌트는 각각 독립적인 코드를 가지고있고, 컴포넌트에서 수정을하면 해당 컴포넌트를 불러오는 페이지안의 컴포넌트도 수정이 됩니다.</p>
<pre><code class="language-jsx">function Header() {
  return &lt;p&gt;컴포넌트 조합&lt;/p&gt;
}

function Test() {
  return (
    &lt;Header /&gt;
    &lt;h1&gt;컴포넌트끼리 조합&lt;/h1&gt;
  )
}

export default Test</code></pre>
<p>각각 독립적이기 때문에 재사용성과 유지보수성이 높습니다.
그렇기 때문에 각각의 컴포넌트를 목적에 맞게 쪼개는 습관이 필요합니다.</p>
<h2 id="props를-사용하여-데이터-전달하기">Props를 사용하여 데이터 전달하기</h2>
<p>컴포넌트 끼리 Props(속성)을 전달하여 데이터를 전송할 수 있습니다.</p>
<pre><code class="language-jsx">function PropsTest(props: {name: string}) {
  return &lt;h1&gt;{props.name}&lt;/h1&gt;
}

function Page() {
  return &lt;PropsTest name=&quot;데이터 전달 가능&quot; /&gt;
}
export default Page</code></pre>
<h3 id="함수와-컴포넌트의-유사성">함수와 컴포넌트의 유사성</h3>
<p>js의 함수처럼 데이터를 받을 수 있습니다. jsx는 js처럼 매개변수를 받아 수행하고 결과값을 내놓습니다.</p>
<h3 id="props는-컴포넌트의-매개변수">Props는 컴포넌트의 매개변수</h3>
<p>Props는 상자안에 넣는 다양한 도구처럼 작동합니다.</p>
<pre><code class="language-jsx">function PropsTest() {
  return (
    &lt;&gt;
      &lt;Header name=&quot;내용A&quot; /&gt;
      &lt;Header name=&quot;내용B&quot; /&gt;
    &lt;/&gt;
  )
}</code></pre>
<p>이렇게 <code>name</code>의 props를 어떻게 넣느냐에 따라 표출되는 값이 바뀝니다.
하지만 <code>Header</code>안에 props값을 지정해버리면 밖에서 아무리 변경해도 <code>Header</code>컴포넌트 안의 props를 사용하기 때문에 값이 변경되지 않습니다.</p>
<p>리액트의 철학이 하나 있습니다. 바로 <strong>단방향 데이터 흐름(one-way data flow)입니다</strong> 리액트는 부모 &gt; 자식으로 데이터가 흐르게끔 되어있습니다.</p>
<h2 id="state를-사용하여-데이터-변경">State를 사용하여 데이터 변경</h2>
<p>부모컴포넌트에서 state(상태)를 관리하고 Props를 통해 자식은 UI만 담당합니다. 이를통해 동적인 UI를 구성할 수 있습니다.</p>
<h2 id="컴포넌트-필수-규칙">컴포넌트 필수 규칙</h2>
<h3 id="컴포넌트-이름은-대문자로-시작">컴포넌트 이름은 대문자로 시작</h3>
<p>JSX는 컴포넌트와 HTML태그를 구분할때 이름의 첫 글자가 대문자이면 JSX, 소문자이면 html로 구분합니다.</p>
<h3 id="컴포넌트는-반드시-하나의-최상위-요소를-반환한다">컴포넌트는 반드시 하나의 최상위 요소를 반환한다</h3>
<pre><code class="language-jsx">return &lt;p&gt;태그1&lt;/p&gt;&lt;strong&gt;태그2&lt;/strong&gt; // 에러

return &lt;&gt; &lt;p&gt;태그1&lt;/p&gt;&lt;strong&gt;태그2&lt;/strong&gt; &lt;/&gt; // 성공</code></pre>
<p>여러개의 요소를 내보낼 수 없다. 반드시 하나의 부모요소 안에 감싸져있어야한다.</p>
<h3 id="훅hook은-최상위에서만-호출-가능하다">훅(hook)은 최상위에서만 호출 가능하다</h3>
<pre><code class="language-jsx">// 최상위에서 호출 O
function App() {
  const [test, setTest] = useState()
  return ...
} 
// 최상위 호출 X -&gt; 에러발생
function App() {
  if(a) {
    const [test, setTest] = useState()
  }
  return ...
}</code></pre>
<p>훅은 함수 컴포넌트의 상태나 생명주기를 관리하는 기능입니다. 사용시 두가지 규칙을 지켜야합니다.</p>
<p><strong>1. 훅은 컴포넌트 최상위에서만 호출해야합니다.</strong>
<strong>2. 훅은 리액트 함수 컴포넌트 또는 커스텀 훅 내부에서만 호출할 수 있습니다.</strong></p>
<h1 id="🔥-state와-props">🔥 State와 Props</h1>
<p>Props는 부모 -&gt; 자식으로 데이터를 전달 할 수단이고, state는 컴포넌트 내부에서 데이터를 관리하는 역할입니다.</p>
<h2 id="state---컴포넌트-내부의-동적인-상태">State - 컴포넌트 내부의 동적인 상태</h2>
<p>state는 컴포넌트 내부에서 정의되며 <strong>사용자 상호작용</strong>, <strong>비동기 데이터 처리</strong>등 사용됩니다. 해당값이 바뀌면 react는 재렌더링하여 화면을 다시 그려줍니다. </p>
<ul>
<li><strong>동적 데이터 관리:</strong> 실시간으로 변할 수 있는 데이터 관리</li>
<li><strong>컴포넌트 내부 전용:</strong> 외부에서 접근 / 수정 X</li>
<li><strong>재렌더링 트리거:</strong> state가 변경되면 해당 컴포넌트만 변경</li>
</ul>
<h2 id="props---컴포넌트간의-데이터-전달">Props - 컴포넌트간의 데이터 전달</h2>
<p>부모가 자식에게 전달할때 사용됩니다. 해당값은 자식에게 전달하여 UI등을 그려내는데 사용합니다.</p>
<ul>
<li><strong>부모-&gt;자식 방향 전달:</strong> 자식이 부모를 수정할 수 없습니다.</li>
<li><strong>재사용성 향상:</strong> 같은 컴포넌트여도 Prop에 따라 다르게 사용가능합니다.</li>
<li><strong>예측 가능한 데이터 흐름:</strong>: 전달받은값에 따라 정해지므로 추적이 용이함</li>
</ul>
<h2 id="state와-props의-차이">State와 Props의 차이</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>Props</th>
<th>State</th>
</tr>
</thead>
<tbody><tr>
<td>정의</td>
<td>부모 컴포넌트가 자식에게 전달하는 데이터</td>
<td>컴포넌트 내부에서 자체적으로 관리되는 데이터</td>
</tr>
<tr>
<td>소유권</td>
<td>부모 컴포넌트</td>
<td>컴포넌트 자신</td>
</tr>
<tr>
<td>변경 가능 여부</td>
<td>읽기 전용</td>
<td>변경가능</td>
</tr>
<tr>
<td>변경시</td>
<td>부모가 다시 전달해야함</td>
<td>useState등을 통해 직접 변경</td>
</tr>
<tr>
<td>용도</td>
<td>컴포넌트 설정 및 데이터 전달</td>
<td>컴포넌트의 상태(입력값, 토글등)관리</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터3 - 덕타이핑, 유틸리티 타입]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%EB%8D%95%ED%83%80%EC%9D%B4%ED%95%91-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%EB%8D%95%ED%83%80%EC%9D%B4%ED%95%91-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 16 Jun 2026 01:28:58 GMT</pubDate>
            <description><![CDATA[<h2 id="덕타이핑ducktyping">덕타이핑(DuckTyping)</h2>
<p>객체의 실제 타입이 아니라 그 객체가 가진 메서드나 속성에 따라 결정된다는 프로그래밍 철학입니다.</p>
<blockquote>
<p>&quot;만약 그것이 오리처럼 걷고 오리처럼 꽉꽉거리면 그것은 오리다.&quot;
미국의 시인 &quot;제임스 위스컴 라일리&quot;</p>
</blockquote>
<p>즉 프로그래밍에서는 어떤 객체가 <strong>오리 클래스</strong>로 만들어졌는지 중요하지 않습니다. 그 객체가 <code>quack</code>이라는 메서드를 가지고 있다면 오리로 인정한다는 뜻입니다.</p>
<pre><code class="language-js">const creditCard = {
  pay: (amount) =&gt; console.log(`신용카드로 ${amount}원 결제 완료!`)
}

const kakaoPay = {
  pay: (amount) =&gt; console.log(`카카오페이로 ${amount}원 결제 완료!`)
}

// 결제 처리 함수 (덕 타이핑 적용)
function processPayment(paymentMethod, amount) {
  if (paymentMethod &amp;&amp; typeof paymentMethod.pay === &#39;function&#39;) {
    paymentMethod.pay(amount)
  } else {
    console.error(&quot;결제할 수 없는 수단입니다.&quot;)
  }
}

processPayment(creditCard, 50000)
processPayment(kakaoPay, 30000)</code></pre>
<p>객체에 pay가 있는지만 확인합니다.</p>
<h3 id="덕-타이핑과-타입-좁히기">덕 타이핑과 타입 좁히기</h3>
<p>덕타이핑의 문제는 객체가 어던 구조를 가지고 있는지 판별하기가 어렵습니다. 이럴때 타입좁히기를 사용합니다.</p>
<p>즉 타입을 유연하게 판단할 수 있는게 덕 타이핑이라면, 타입좁히기는 그 유연한 타입을 안전하게 분기하고 사용할 수 있도록 도와주는 기술입니다.</p>
<pre><code class="language-js">// processId는 string이거나 number일 수 있음
function processId(id: string | number) {
  // id.toUpperCase(); // 에러 - number일 수도 있기때문

  // typeof - 타입좁히기
  if (typeof id === &#39;string&#39;) {
    // 무조건 string일 것이기 때문에 사용가능
    console.log(id.toUpperCase());
  } else {
    // 자동으로 &#39;number&#39; 타입
    console.log(id.toFixed(2)); // 사용 가능
  }
}</code></pre>
<h3 id="덕-타이핑과-인터페이스">덕 타이핑과 인터페이스</h3>
<p>덕타이핑은 구조만 맞으면 타입으로 간주하기때문에 아주 유연하지만, 코드 가독성을 떨어트리고 의도를 모호하게 만들 수 있습니다.</p>
<p>이럴때 interpace를 활용해 코드의 일관성과 안정성을 높힐 수 있습니다.</p>
<pre><code class="language-ts">interface Flyer {
  fly(): void
}

//  Flyer의 규칙을 지키겠다고 선언
class Airplane implements Flyer {
  fly() { console.log(&quot;비행기가 엔진을 켜고 날아갑니다.&quot;); }
}

class Bird implements Flyer {
  fly() { console.log(&quot;새가 날개를 퍼덕이며 날아갑니다.&quot;); }
}

// Flyer 인터페이스를 만족하는 객체만 받겠다고 선언
function makeItFly(target: Flyer) {
  target.fly();
}

makeItFly(new Airplane())
makeItFly(new Bird())</code></pre>
<h2 id="유틸리티-타입">유틸리티 타입</h2>
<p>유틸리티 타입을 활용하면, 타입선언을 더 간결하고 유연하게 만들 수 있습니다.
대표적인 유틸리티 타입으로는<code>Partial</code>,<code>Required</code>,<code>Readonly</code>,<code>Pick</code>,<code>Omit</code>,<code>Record</code>등이 있습니다.</p>
<h3 id="유틸리티-타입-분석">유틸리티 타입 분석</h3>
<p>유틸리티 타입은 단순한 축약 문법이 아닙니다. 타입 스크립트의 핵심 기능인 제네릭과 메핑된 타입을 바탕으로 동작합니다.</p>
<p>대표 유틸리티 타입 중 하나인 <code>Partial&lt;&#39;T&#39;&gt;</code>의 내부 구조를 확인해봅시다.</p>
<pre><code class="language-ts">type Partial&lt;T&gt; = {
  [P in keyof T]?:T[p]
}</code></pre>
<p>분리해서 해석해보겠습니다</p>
<pre><code class="language-ts">keyof T</code></pre>
<p>T 타입의 모든 키를 불러옵니다. T가 {name: string, age:number}일경우 keyof T는 name, age와 같은 유니언타입이 됩니다.</p>
<blockquote>
<p><strong>유니언(Union) 타입이란?</strong>
타입스크립트에서 | (파이프) 기호로 연결된 타입을 말하며, <strong>&quot;이 중에 하나&quot;</strong>라는 뜻입니다. 즉, &quot;name&quot; | &quot;age&quot;는 <strong>&quot;문자열 &#39;name&#39; 또는 문자열 &#39;age&#39; 둘 중 하나만 올 수 있어&quot;</strong>라는 뜻이 됩니다.</p>
</blockquote>
<pre><code class="language-ts">[P in keyof T]</code></pre>
<p>T의 각 키를 하나씩 순회하며 P라는 이름으로 바인딩 합니다. 즉 <code>[P in ...]</code> 이라는 맵드 타입(Mapped Type) 문법을 사용해서, 그 <strong>유니언 타입에 있는 키들을 하나씩 순회(반복문처럼 루프)</strong>합니다.</p>
<pre><code class="language-ts">?:T[P]</code></pre>
<p>P라는 키에 타입을 설정하는데 ?를 붙이면 옵셔널로 지정하겠다는 뜻입니다.</p>
<p>즉</p>
<pre><code class="language-ts">type User = {
  name: string
  age: number
}

//Partial&lt;User&gt;
{
  name?: string
  age?: number
}</code></pre>
<h3 id="대표-유틸리티-타입">대표 유틸리티 타입</h3>
<p>방금 살펴본<code>Partial</code>말고도 <code>Required</code>,<code>Readonly</code>,<code>Pick</code>,<code>Omit</code>,<code>Record</code>등이 있습니다.</p>
<h3 id="partial-t">Partial&lt; T&gt;</h3>
<p>기존 타입의 모든 속성을 선택적(optioal)로 바꿉니다.
주로 수정요청(Requeset Body)이나 부분 상태 업데이트에서 사용됩니다.</p>
<pre><code class="language-ts">type User = {
  id: number
  name: string
}

const patchUser: Partial&lt;User&gt; {
  name: &quot;bob&quot; 
  // id는 생략가능
}</code></pre>
<h3 id="requied-t">Requied&lt; T&gt;</h3>
<p>모든 속성을 필수(Requied)로 바꿉니다. Partial의 반대입니다.</p>
<pre><code class="language-ts">type Config = {
  debug?: boolean
}

const strictConfig: Requied&lt;Config&gt; {
  debug: true // 안넣으면 오류가 남</code></pre>
<h3 id="picktk">Pick&lt;T,K&gt;</h3>
<p><strong>선택한 것만 가져오겠다</strong>라는 뜻입니다. 타입(T)에서 일부 속성만 선택(pick)하여 새로운 타입을 만듭니다. </p>
<pre><code class="language-ts">type UserPreview = Pick&lt;User,&quot;ID&quot; | &quot;name&quot;&gt;</code></pre>
<h3 id="omittk">Omit&lt;T,K&gt;</h3>
<p><strong>선택한 것만 제외(Omit)하고 가져오겠다라</strong>는 뜻입니다.
비밀번호같이 민감한 속성을 제외할때 사용됩니다.</p>
<pre><code class="language-ts">const omitUser = Omit&lt;User, &quot;password&quot;&gt;</code></pre>
<h3 id="recordkt">Record&lt;K,T&gt;</h3>
<p><strong>키-값의 매핑구조를 만들겠다</strong>는 의미입니다.
저장된 키 집합 K에대해 타입 T를 가지는 객체 타입을 생성합니다.</p>
<pre><code class="language-ts">type StatusMap = Record&lt;&quot;loading&quot;|&quot;success&quot;|&quot;error&quot;, boolean&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[LinkPet 트러블 슈팅]]></title>
            <link>https://velog.io/@songha-5/LinkPet-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@songha-5/LinkPet-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85</guid>
            <pubDate>Thu, 11 Jun 2026 08:23:06 GMT</pubDate>
            <description><![CDATA[<h1 id="트러블-슈팅">트러블 슈팅</h1>
<p>작업하면서 여러 문제들을 만났습니다. 해당 문제들을 정리하여 다음 작업의 기반으로 만들고자 합니다.</p>
<h2 id="1-메일로-받은-링크에서-비밀번호-페이지로-연결이-안되는-문제">1. 메일로 받은 링크에서 비밀번호 페이지로 연결이 안되는 문제</h2>
<h3 id="문제">문제</h3>
<p>비밀번호 변경 메일을 보냈을때 비밀번호 변경 링크가 메일로 전송되고, 해당 링크를 누르면 비밀번호 변경 페이지로 이동이 되어야합니다. 
하지만 메일을 열었을때 비밀번호 변경 페이지로 이동하지 못하고 로그인화면(메인)으로 이동이 되는 문제가 있었습니다.</p>
<h3 id="문제-원인">문제 원인</h3>
<p>Supabase의 인증 흐름과 미들웨어 설정의 충돌로 일어난 문제였습니다.</p>
<h4 id="미들웨어-접근-제어-충돌">미들웨어 접근 제어 충돌</h4>
<p>Supabase에서 비밀번호 변경을 할 때 <strong>임시로 세션(로그인)상태를 부여</strong>합니다. 하지만 middlewear에서 로그인한 유저는 비밀번호 변경 페이지에 접근 할 수 없도록 막아두었습니다. 그래서 비밀번호 변경 페이지로 진입시 미들웨어에따라 비밀번호 변경 페이지로 이동하지 못하였습니다.</p>
<pre><code class="language-ts">// 인증 없이 접근 가능한 경로
const PUBLIC_PATHS = [
  &#39;/auth/password&#39;,
  &#39;/auth/password/change&#39;, // 비밀번호변경 페이지 접근 가능
  &#39;/auth/password/result&#39;,
  &#39;/auth/join&#39;,
  &#39;/auth/join/result&#39;,
  &#39;/api/auth/callback&#39;
]

// 로그인된 유저가 접근하면 안되는 경로
const GUEST_ONLY_PATHS = [
  &#39;/&#39;,
  &#39;/auth/password&#39;,
  &#39;/auth/password/change&#39;, // 비밀번호변경 페이지 접근 불가능
  &#39;/auth/join&#39;,
  &#39;/api/auth/callback&#39;
]</code></pre>
<h4 id="잘못된-리다이렉트-경로">잘못된 리다이렉트 경로</h4>
<p>Next.js 서버 컴포넌트 환경에서는 URL에 포함된 인증(code)를 서버의 세션으로 교환해야 하는 과정이 필수입니다. 그런데 교환없이 바로 해당 페이지로 이동하는 코드를 넣어 쿠키를 굽지않고(callback) 넘어가서 문제가 생겼습니다</p>
<blockquote>
<p><strong>왜 code를 cookie로 변경해야하나요?</strong>
code는 URL에 넣어서 들어옵니다. callback은 해당 code를 cookie로 변경해줍니다. 
서버에서는 localStorage를 확인 할 수 없습니다. 그래서 클라이언트측에서 코드를 보내면 서버에서 쿠키를 만들어 클라이언트에 다시 보내줍니다. 서버측에서 해당 유저가 로그인했는지 안했는지 여부를 확인 할 수 있습니다.</p>
</blockquote>
<h4 id="콜백callbackapi의-하드코딩된-라우팅">콜백(callback)API의 하드코딩된 라우팅</h4>
<p>콜백 API가 쿠키 교환 성공 후 페이지 이동을 해야하는데, 하드코딩한 페이지로 바로 넘어가서 로그인한지 여부를 몰라 게스트로 취급했습니다.</p>
<pre><code class="language-ts">const { error } = await supabase.auth.resetPasswordForEmail(
    data.email,
    {
      redirectTo: `${getURL()}auth/password/change`  // 하드코딩된 페이지
    }
  )</code></pre>
<h2 id="문제해결">문제해결</h2>
<h4 id="미들웨어-수정">미들웨어 수정</h4>
<pre><code class="language-ts">// 로그인된 유저가 접근하면 안되는 경로
const GUEST_ONLY_PATHS = [
  &#39;/&#39;,
  &#39;/auth/password&#39;,
  // &#39;/auth/password/change&#39;, &lt;&lt; 해당 줄 삭제
  &#39;/auth/join&#39;,
  &#39;/api/auth/callback&#39;
]</code></pre>
<p>접근을 허용하도록 해당 경로의 제어를 풀었습니다. 이제 로그인한 유저도 비밀번호 변경 페이지에 진입할 수 있습니다.</p>
<h4 id="메일전송-action-수정">메일전송 Action 수정</h4>
<pre><code class="language-ts">const { error } = await supabase.auth.resetPasswordForEmail(
    data.email,
    {
      // 바로 페이지 이동(쿠키없이)을 하지않고, 쿠키를 구워주는 콜백으로 이동
      redirectTo: `${getURL()}api/auth/callback?next=/auth/password/change`
    }
  )</code></pre>
<p>바로 페이지 이동을 하지않고, 콜백을 통해 쿠키를 구워줄 수 있게하여 서버 및 클라이언트가 접근한 유저가 게스트인지 회원인지 구분 할 수 있도록 하였습니다.</p>
<h4 id="콜백-api-수정">콜백 API 수정</h4>
<pre><code class="language-ts">export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url)
  const code = searchParams.get(&#39;code&#39;)

  // URL에서 next 파라미터를 추출 (없으면 기본값 &#39;/user&#39;) = 메인
  const next = searchParams.get(&#39;next&#39;) ?? &#39;/user&#39;

  if (code) {
    const supabase = await createClient()
    const { error } = await supabase.auth.exchangeCodeForSession(code)

    if (!error) {
      // 쿠키 굽기 성공 시, next에 담긴 경로로 리다이렉트
      return NextResponse.redirect(`${origin}${next}`)
    }
  }

  return NextResponse.redirect(`${origin}/`)
}</code></pre>
<p>기존에는 무조건 user로 이동하게 되어있었으나, 비밀번호 변경에도 사용할 수 있도록 유동적으로 변경하였습니다.</p>
<h2 id="느낀점배운점">느낀점/배운점</h2>
<p>어떤 흐름으로 비밀번호 변경을 하는지에 대해 배웠습니다. 어떻게 해당 <strong>유저가 자기 비밀번호만 바꿀 수 있는지</strong> 알 수 있었고, 그 로직에 맞게 코드를 고려하고 짜야한다는 점을 배웠습니다. 그리고 <strong>callback과 code, cookie</strong>에 대해 알 수 있었습니다. react와 달리 next.js는 localstorge에 바로 저장을 하지 않고 보안을 위해 code와 callback을 이용하여 유저가 로그인했는지 안했는지를 구분하여 작동한다는점을 알게되어 큰 이득이 있었습니다. 이번 오류에서 어떻게 Next.js가 작동하는지 배울 수 있어 좋은 기회였다고 생각합니다.</p>
<h1 id="2-미들웨어-내-getuser-블로킹">2. 미들웨어 내 getUser() 블로킹</h1>
<h3 id="문제-1">문제</h3>
<p>mac에서 window로 개발 환경을 바꾸었습니다. 바꾸고 난 뒤, 작업 직후에는 문제가없었지만 시간이 지나고 난 뒤 화면이 불러와지지 않으며, 무한 로딩이 도는 버그가 일어났습니다.</p>
<h3 id="문제원인">문제원인</h3>
<p>미들웨어에 쓰인 <code>getUser()</code>때문이였습니다. 
<code>getUser</code>의 문제였습니다. 미들웨어에서 페이지가 전환될때마다 매번 데이터를 불러서 검증하고있었습니다. 하지만 맥환경에서 window와 다르게 <code>getUser()</code>가 응답없이 멈추어버려서 에러없이 무한 로딩에 걸린거였습니다.</p>
<p>그리고 맥에서만 발생하는 이유는 <strong>맥은 윈도우와 다르게 IPv6주소로 먼저 접근하고 안되면 IPv4로 넘어가는데</strong> IPv6에서 주소를 계속해서 불러오는 상황이 되어 무한 로딩에 걸렸던것이였습니다. </p>
<h2 id="문제해결-1">문제해결</h2>
<p>미들웨어에 쓰인 <code>getUser()</code>삭제 후 <code>getSession()</code>으로 전환하였습니다.</p>
<pre><code class="language-tsx">// getUser삭제 후 getSession 추가
const { data: { session } } = await supabase.auth.getSession()
  const user = session?.user ?? null

  const currentPath = request.nextUrl.pathname

  const isPublicPath = currentPath === &#39;/&#39; || PUBLIC_PATHS.some((path) =&gt; currentPath.startsWith(path))

  const isGuestOnlyPath = GUEST_ONLY_PATHS.some((path) =&gt;
    currentPath === path
  )


  // 비로그인 유저가 보호된 페이지 접근 시 메인으로
  if (!user &amp;&amp; !isPublicPath) {
    const url = request.nextUrl.clone()
    url.pathname = &#39;/&#39;
    return NextResponse.redirect(url)
  }

  // 로그인 유저가 auth관련 페이지 접근 시 홈으로
  if (user &amp;&amp; isGuestOnlyPath) {
    const url = request.nextUrl.clone()
    url.pathname = &#39;/user&#39;
    return NextResponse.redirect(url)
  }</code></pre>
<p><code>getSession</code>은 매번 데이터를 불러오는 <code>getUser</code>와 다르게 쿠키에 저장된 세션정보를 로컬에서 읽습니다. 매번 로딩하지않기때문에 가볍고, 빠르게 작동합니다.</p>
<blockquote>
<ul>
<li><strong>getUser():</strong> supabase 서버에 매번 네트워크 요청, 보안이 좋음, 느림</li>
</ul>
</blockquote>
<ul>
<li><strong>getSession():</strong> 쿠키에 저장된 세션 정보를 로컬에서 읽음, 보안이 나쁨, 빠름</li>
</ul>
<p>middlewear는 <strong>라우팅분기용이기 때문에 가벼워야합니다.</strong> 이런점에서 <code>getUser()</code>는 어울리지 않습니다.</p>
<p>그리고 맥에서 발생하는 문제를 고치기 위해 할 수 있는 몇가지 방법이 있습니다.
&quot;미들웨어에서 데이터로딩이 오래걸리면 ~하라&quot; 라는 코드가있으면 페이지 전환은 됬겠지만 최악의 사용자 경험을 주게됩니다.
그리고 다른방법은 위에 말씀드렸던것처럼 쿠키에서 세션을 가져와 로컬로 처리하면 해결이 됩니다. ( supabase에서 매번  토큰 정보의 검증을 요청하느냐, 쿠키에 저장된 토큰을 사용하나의 차이 )</p>
<h2 id="느낀점배운점-1">느낀점/배운점</h2>
<p><code>getSession</code>에 대해 알 수 있어 다행이라고 생각한다. 미들웨어는 무조건 가볍게 해야한다는점을 알 수 있었으며 그리고 <code>getSession</code>과 <code>getUser</code>의 차이점 도 알 수 있었다. 만약 계속 윈도우로 작업했다면 비효율적으로 데이터를 매번 불러왔다는것을 몰랐을 것이다. 아주 좋은 계기가 되었다고 생각하였다.</p>
<h1 id="멀티스탭-폼-구현">멀티스탭 폼 구현</h1>
<h3 id="문제-2">문제</h3>
<p>한페이지 안에서 폼만 화면이 전환되고, 특정 선택지를 누르면 그 선택지에 관련한 페이지가 구현되도록 하려했습니다. 
하지만 페이지 전환했을때 기존 선택지가 선택 해제되거나, 많은 데이터들을 저장하고 마지막에 완료 페이지를 눌러야 데이터가 전송되게끔 코드를 작성해야했습니다. 하지만 페이지수가 많아 한번에관리하기 힘들었습니다.</p>
<h3 id="문제원인-1">문제원인</h3>
<p>페이지수가 많아 한번에 관리하기 힘들었으며, 컴포넌트끼리의 데이터 전달 흐름(Props driling)을 알아보기 쉽게 정리해야했습니다. 그리고 한 컴포넌트안에 여러 컴포넌트로 페이지 전환을 하다보니, 페이지의 진행 상황도 저장하면서(데이터 보존), 그 전의 데이터가 날아가지 않게 신경써야했으며, 페이지 좌우 이동시 그 전에 작성한 데이터들이 form에 남아있어야했습니다.  </p>
<h2 id="문제해결-2">문제해결</h2>
<pre><code class="language-tsx">&lt;FormProvider {...methods}&gt;
  &lt;Link href={&#39;/user&#39;} className=&quot;inline-block cursor-pointer transition-all hover:border-neonPink hover:text-neonPink border-5 border-font-white py-2 px-4&quot; aria-label=&quot;컨트롤 룸으로 돌아가기&quot;&gt;◀ 컨트롤 룸 복귀 (BACK)&lt;/Link&gt;

  { /* 현재 페이지 정보 */}
  &lt;form className=&quot;mbs-4&quot; onSubmit={methods.handleSubmit(onSubmit)}&gt;
    ...
  &lt;/form&gt;
 &lt;/FormProvider&gt;
</code></pre>
<p>이번에 적용한 react-from-hook을 사용하여 위의 문제점들을 해결하였습니다. <code>FormProvider</code>를 활용해 부모컴포넌트에서 자식컴포넌트로 데이터전달을 쉽게하였습니다. 그리고 부모컴포넌트에서 <code>watch</code>를 사용해 사용자가 현재값을 치는지, 입력해야할 값들을 다 입력했는지 확인하여 버튼을 disabled을 해제합니다. 그리고 react-hook-form을 사용했기때문에 부모컴포넌트에서 모든 값을 관리합니다. 그래서 자동으로 페이지 전환을 했을때 해당값이 남아있도록 할 수 있었습니다.</p>
<pre><code class="language-tsx">&lt;div&gt;
  &lt;h2 className=&quot;text-center text-2xl text-font-white text-shadow-[3px_3px_0_var(--color-neonPink)]&quot;&gt;CHECK_LIST //&lt;br /&gt;귀 &amp; 피부 검진하기&lt;/h2&gt;

  &lt;div className=&quot;mbs-10&quot;&gt;
    &lt;BaseCheckbox value={CHECK_SKIN} none noneContent=&quot;없음&quot; {...register(&#39;skin&#39;) }/&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<h2 id="느낀점배운점-2">느낀점/배운점</h2>
<p>데이터 흐름 설계가 중요하단걸 배웠습니다. 단순히 부모 컴포넌트에서 자식으로 데이터를 넘기는것을 넘어, 여러 단계에 걸친 데이터를 한곳에서 관리하려면 체계적인 상태 관리가 필요하단점을 알 수있는 계기였습니다.
라이브러리를 남용하면 좋지않지만, 상황에 적절한 라이브러리를 찾고 그것을 적절히 사용해야한다는 점을 배웠습니다. 모든 기능을 처음부터 직접 구현하는것도 좋지만, 시간이 한정되어있는 상황에서는 상황에 맞는 라이브러리를 사용하는것이 보다 효율적임을 알았습니다. 코드가 꼬이는 상황을 줄이고, 유지 보수에도 좋다는점을 배웠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터3 - 타입 별칭과 인터페이스, 제네릭]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD%EA%B3%BC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%9C%EB%84%A4%EB%A6%AD</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%ED%83%80%EC%9E%85-%EB%B3%84%EC%B9%AD%EA%B3%BC-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%9C%EB%84%A4%EB%A6%AD</guid>
            <pubDate>Thu, 11 Jun 2026 02:41:24 GMT</pubDate>
            <description><![CDATA[<h1 id="🔍-타입-별칭과-인터페이스">🔍 타입 별칭과 인터페이스</h1>
<p>자바스크립트는 구조를 명시적으로 표현할 수 없지만 타입스크립트는 구조에 타입을 지정할 수 있습니다.</p>
<p>타입 별칭(type alias)와 인터페이스(interface)가 있습니다.</p>
<h2 id="타입-별칭-type-alias">타입 별칭 (type alias)</h2>
<pre><code class="language-ts">type User = {
  age: number
  name: string
}
type Role = {
  role: &quot;admin&quot; | &quot;user&quot;
}
type Point = [number, number]</code></pre>
<p>타입에 이름을 붙여 관리할 수 있습니다.
고급 타입을 활용 할 수 있으나, <strong>재선언이나 확장이 불가능</strong>합니다.</p>
<pre><code class="language-ts">type name = {
  name: string
}
type name = {  
  age: number
}
// 에러가 납니다. 동일한 name을 재선언했기 때문입니다. type은 재선언이 불가능합니다.
</code></pre>
<h2 id="인터페이스interface">인터페이스(interface)</h2>
<p>객체의 형태를 정의하기 위한 전용 문법입니다.</p>
<pre><code class="language-ts">interface Uers {
  name: string
  age: number
}

const user: User {
  name: &quot;이름&quot;,
  age: 30
}</code></pre>
<ul>
<li>확장이 가능합니다 (interface merge)</li>
<li>클래스와 함께 사용가능합니다</li>
<li>라이브러리 타입 선언에 자주 사용됩니다.<pre><code class="language-ts">inferface A {
name: string
}
interface A {
age: number
}
// 두번 선언해도 되고 A는 name과 age를 함께 가집니다. </code></pre>
같은 이름으로 사용할 수 있고, 그렇게되면 합쳐집니다.</li>
</ul>
<pre><code class="language-ts">interface Person {
  name: string
}
interface Age extends Person {
  age: number
}

// age는 name과 age를 동시 사용 가능</code></pre>
<p>extends를 이용해 A interface와 B interface를 합쳐 사용이 가능 합니다.</p>
<h2 id="타입-별칭-vs-인터페이스">타입 별칭 vs 인터페이스</h2>
<p>두개다 거의 동일합니다. 팀의 컨벤션 따라 정의할 수 있습니다.
추천하는 방식은 객체의 구조를 정의할때나 클래스가 해당 구조를 구현하는 경우는 interface, 튜플, 유니언 등 타입조합은 type만 가능하니 type을 사용하는게 좋습니다.</p>
<h2 id="실무에서-타입관리">실무에서 타입관리</h2>
<h3 id="중앙화된-타입-관리">중앙화된 타입 관리</h3>
<pre><code>src/
├── components/
│   ├── UserProfile.tsx
│   └── ProductCard.tsx
├── api/
│   ├── userApi.ts
│   └── productApi.ts
├── types/                 &lt;-- 모든 타입이 모이는 중앙 폴더
│   ├── index.ts           (타입들을 모아서 export 하는 용도)
│   ├── common.ts          (Pagination, API Response 등 공통 타입)
│   ├── user.ts            (User 관련 타입: User, UserRole 등)
│   └── product.ts         (Product 관련 타입)
└── App.tsx</code></pre><p>타입 안에 모아서 관리하는 방식입니다.
타입 재사용성이 높고, 타입을 한 눈에 볼 수 있어 유지보수에 좋습니다.</p>
<h3 id="모듈별로-타입-관리">모듈별로 타입 관리]</h3>
<pre><code>src/
├── features/              &lt;-- 기능(도메인)별로 폴더를 나눔
│   ├── user/
│   │   ├── components/
│   │   │   └── UserProfile.tsx
│   │   ├── userApi.ts
│   │   └── types.ts       &lt;-- 오직 User 모듈에서만 쓰는 타입
│   │
│   └── product/
│       ├── components/
│       │   └── ProductCard.tsx
│       ├── productApi.ts
│       └── types.ts       &lt;-- 오직 Product 모듈에서만 쓰는 타입
│
├── shared/                &lt;-- 여러 모듈에서 공통으로 쓰는 것만 분리
│   └── types/
│       └── common.ts      (공통 API 응답 타입 등)
│
└── App.tsx</code></pre><p>타입이 모듈 근처에 위치하여 직관적입니다. 그리고 모듈단위로 작업하는 팀프로젝트에 좋습니다.
하지만 중복선언이나, 반복선언의 위험성이 있습니다.</p>
<h3 id="두-타입-관리-비교">두 타입 관리 비교</h3>
<p>관리 방식|장점|단점|추천하는 환경
|---|---|---|---
중앙화|파일들이 한 곳에 있어 전체 타입을 빠르게 훑어보기 편함|프로젝트가 커지면 types 폴더가 비대해지고 복잡해짐|소규모 ~ 중규모 
프로젝트 모듈별|코드를 수정할 때 관련 타입 파일이 바로 옆에 있어 유지보수가 매우 직관적임|이 타입이 공통인지, 특정 모듈 전용인지&quot; 분류하는 기준이 초반에 모호할 수 있음|대규모 프로젝트, 도메인(기능) 단위 개발</p>
<h3 id="추천방식---하이브리드">추천방식 - 하이브리드</h3>
<pre><code>src/
├── types/                 &lt;-- [중앙화] 앱 전체에서 공유되는 글로벌/공통 타입
│   ├── index.ts           (공통 타입들을 모아서 쉽게 import 하도록 돕는 파일)
│   ├── api.ts             (API 공통 응답 구조, Pagination, Error 타입 등)
│   ├── ui.ts              (테마, 공통 모달 상태 등 전역 UI 관련 타입)
│   └── global.d.ts        (전역 환경 변수나 외부 라이브러리 타입 선언)
│
├── features/              &lt;-- [모듈별] 비즈니스 로직 / 도메인 영역
│   ├── auth/
│   │   ├── components/
│   │   │   └── LoginForm.tsx
│   │   ├── authApi.ts
│   │   └── types.ts       &lt;-- &#39;인증(auth)&#39; 모듈 안에서만 사용하는 타입 (LoginPayload 등)
│   │
│   └── product/
│       ├── components/
│       │   └── ProductList.tsx
│       ├── productApi.ts
│       └── types.ts       &lt;-- &#39;상품(product)&#39; 모듈 안에서만 사용하는 타입 (ProductItem 등)
│
├── components/            &lt;-- 앱 전체에서 재사용되는 공통 UI 컴포넌트
│   └── Button/
│       ├── Button.tsx
│       └── types.ts       &lt;-- Button 컴포넌트 전용 Props 타입 (ButtonVariant 등)
│
└── App.tsx</code></pre><p>공통성과 재사용성을 동시에 확보할 수 있습니다.</p>
<h1 id="💻-제네릭generic">💻 제네릭(Generic)</h1>
<p>제네릭은 타입에 이름을 붙여 재사용 가능하게 만드는 기능입니다. 타입을 변수처럼 이름을 붙여 쓰는 기능입니다.</p>
<pre><code class="language-ts">funtion identity&lt;T&gt;(value: T) {
  return value
}

identity&lt;string&gt; = &quot;문자열&quot; // T는 문자로 추정
identity&lt;number&gt; = 111  // T는 숫자로 추정

// 제네릭에 기본값 지정
type generic&lt;T = string&gt; = T | undefined</code></pre>
<p>제네릭에도 기본값을 설정 할 수 있습니다.</p>
<h2 id="제네릭의-활용">제네릭의 활용</h2>
<p>T는 지정된 이름이 아니고 ABCD처럼 지정 할 수 있습니다. 하지만 일반적으로 대문자 한글자를 쓰는게 일반적인 컨벤션입니다.</p>
<pre><code class="language-ts">// type활용 
type Wrapper&lt;T&gt; = {
  data: T
}

// interface 활용
interface ApiResponse&lt;T&gt; {
  status: number
  payload: T
}

const response: ApiResponse&lt;string&gt; {
  status: 200
  payload: &quot;성공&quot;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터3 - 타입시스템]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%ED%83%80%EC%9E%85%EC%8B%9C%EC%8A%A4%ED%85%9C</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B03-%ED%83%80%EC%9E%85%EC%8B%9C%EC%8A%A4%ED%85%9C</guid>
            <pubDate>Thu, 11 Jun 2026 01:27:10 GMT</pubDate>
            <description><![CDATA[<h1 id="타입-시스템">타입 시스템</h1>
<p>들어오는 값이 올바른지 확인하는 것이 매우 중요합니다. 그래서 도입된것이 <strong>타입 시스템 Tpye System</strong>입니다.</p>
<h2 id="동적-타입-시스템과-정적-타입-시스템">동적 타입 시스템과 정적 타입 시스템</h2>
<ul>
<li><strong>동적 타입(dynamic typing)</strong>: 변수를 선언한 다음 값을 할당할때 해당 타입이 정해지는 것을 말합니다. <strong>ex) 자바스크립트</strong></li>
<li><strong>정적 타입(static typing)</strong>: 변수를 선언할때 타입이 정해지는 것을 말합니다. <strong>ex) 타입스크립트</strong></li>
</ul>
<p>동적타입은 해당 타입을 선언한 뒤 지정하기때문에 자유롭고, 초기 개발속도를 빠르게 가져갈 수 있습니다.
하지만 타입실수를 컴파일때 알 수 있기때문에 원인을 찾기 힘들고, 예상하지 못한 런타임 에러가 많습니다.</p>
<p>정적타입은 해당 타입을 선언할때 지정되기때문에 유지보수와 협업에 편하며, 개발 환경에서 오류를 알 수 있기 때문에 빠르게 오류를 잡아낼 수 있습니다. 그래서 코드의 안정성이 높습니다.</p>
<h3 id="타입스크립트-동적과-정적-타입-사이의-균형">타입스크립트: 동적과 정적 타입 사이의 균형</h3>
<p>타입스크립트는 정적 타입이지만 자바스크립트의 장점인 유연성을 적용하였습니다.</p>
<p>그 대표적인 사례가 <code>any</code>입니다.</p>
<pre><code class="language-ts">let data: any = 123
data = &quot;hello&quot; // 가능
data = { key: true } // 가능</code></pre>
<p>하지만 any사용은 타입스크립트의 장점을 죽여버리는 결과가 나옵니다.
any사용은 급한 불을 끄는데 좋지만, 남용한다면 타입스크립트를 사용하는 의미가 없기때문에 사용을 지양합니다.</p>
<p><code>any</code>보다 더 안전한 타입도 제공합니다. <code>unknown</code>이 대표적으로 있습니다.</p>
<p><strong>any 특징</strong></p>
<ul>
<li><p><strong>모든 값을 할당가능</strong>: 숫자, 문자열, 객체, 함수 등 어떤 값이든 unknown 타입의 변수에 넣을 수 있습니다.</p>
</li>
<li><p><strong>타입 확인 전에는 사용할 수 없습니다</strong>: 변수가 unknown 타입일 때는, 그 값의 메서드를 호출하거나 프로퍼티에 접근하려고 하면 TypeScript가 에러를 발생시킵니다.</p>
</li>
<li><p><strong>타입 가드(Type Guard)가 필수적입니다</strong>: typeof나 instanceof 등을 사용해 구체적인 타입으로 좁혀주어야(Narrowing) 비로소 사용할 수 있습니다.</p>
</li>
</ul>
<pre><code class="language-ts">userInput = 100

// 타입 명시 후 사용가능
if (typeof userInput === &quot;number&quot;) {
    console.log(userInput + 50)
}</code></pre>
<p>타입스크립트의 강점은 강제적인 정적 타입 시스템이 아니라, 점진적으로 타입을 도입할 수 있는 유연함입니다.</p>
<h2 id="자바스크립트와-타입스크립트의-기본타입">자바스크립트와 타입스크립트의 기본타입</h2>
<p><strong>자바스크립트</strong></p>
<pre><code class="language-js">// 원시 타입 (Primitive Types)
let myString = &quot;안녕하세요&quot;;     // string
let myNumber = 42;             // number (정수, 실수 모두 포함)
let myBoolean = true;          // boolean (true / false)
let myUndefined = undefined;   // undefined (값이 할당되지 않은 상태)
let myNull = null;             // object (의도적으로 값이 비어있음을 명시. JS의 오래된 버그로 typeof 시 object로 나옴)
let mySymbol = Symbol(&quot;id&quot;);   // symbol (고유하고 변경 불가능한 식별자, ES6 추가)
let myBigInt = 1234567890n;    // bigint (매우 큰 정수, ES2020 추가)

// 객체 타입 (Object Types)
let myArray = [1, 2, 3];                  // object (배열)
let myObject = { name: &quot;JS&quot;, age: 30 };   // object (객체)
let myFunction = function() { return 1; };// function (함수도 일급 객체)</code></pre>
<p><strong>타입스크립트</strong></p>
<pre><code class="language-ts">// 원시 타입 (Primitive Types)

let myString: string = &quot;안녕하세요&quot;;     // 문자열 (string)
let myNumber: number = 42;             // 숫자 (number: 정수, 실수, NaN 등 모두 포함)
let myBoolean: boolean = true;         // 참/거짓 (boolean)
let myUndefined: undefined = undefined; // 값이 아직 할당되지 않은 상태 (undefined)
let myNull: null = null;               // 의도적으로 값이 비어있음을 명시 (null)
let mySymbol: symbol = Symbol(&quot;id&quot;);   // 고유하고 변경 불가능한 식별자 (symbol, ES6 추가)
let myBigInt: bigint = 1234567890n;    // 매우 큰 정수 (bigint, ES2020 추가)</code></pre>
<h2 id="타입스크립트의-고급-타입">타입스크립트의 고급 타입</h2>
<ol>
<li><strong>유니온 타입 (Union Types)</strong>: |
유니온 타입은 값이 지정된 여러 타입 중 하나일 수 있음을 의미합니다. 기호 <code>|</code> (파이프)를 사용합니다.</li>
</ol>
<p><strong>주요 용도</strong>: 함수가 여러 종류의 인자를 받을 수 있어야 하거나, 변수가 상황에 따라 다른 타입의 값을 가질 때 사용합니다.</p>
<ol start="2">
<li><strong>인터섹션 타입 (Intersection Types)</strong>: &amp;
인터섹션 타입은 여러 타입을 하나로 결합하여, 모든 타입의 특성을 다 갖춘 단일 타입을 만듭니다. 기호 <code>&amp;</code> (앰퍼샌드)를 사용합니다.</li>
</ol>
<p><strong>주요 용도</strong>: 기존에 만들어둔 여러 개의 인터페이스나 객체 타입을 조합해서 더 크고 복잡한 객체 타입을 만들 때 주로 사용합니다. (객체의 확장에 유용)</p>
<h3 id="배열-타입array">배열 타입(Array)</h3>
<pre><code class="language-ts">let number: number[] = [1,2,3]
let string: Array&lt;string&gt; = [&quot;1&quot;,&quot;2&quot;,&quot;3&quot;]</code></pre>
<ul>
<li>number[]는 배열 요소의 타입뒤에 대괄호를 붙이는 축약형 문법</li>
<li>Array<string>은 제네릭 형태로 배열을 표현</li>
</ul>
<h3 id="튜플-타입tuple">튜플 타입(Tuple)</h3>
<pre><code class="language-ts">let personTuple: [string, number] = [&quot;Jhon&quot;, 32]</code></pre>
<p>고정된 길이와 각 위치의 타입이 정해진 배열, <strong>순서와 타입</strong> 모두 중요합니다.</p>
<h3 id="열거형-타입enum">열거형 타입(Enum)</h3>
<pre><code class="language-ts">enum Direction {
  Up,
  Down,
  Left,
  Right
}
let dir:Direction = Direction.Up</code></pre>
<p>eume은 연관됭 상수들을 그룹화 하는 문법 코드 가독성을 높히며 매직 넘버를 없애는데 효과적입니다.</p>
<blockquote>
<p>매직넘버 : 의미 있는 이름의 상수로 대체될 수 있는 문자
  <code>add(2.7182718) =&gt; const EULER_NUMBER = 2.1782818</code></p>
</blockquote>
<p><strong>자동 숫자 할당</strong>: TypeScript는 기본적으로 Enum의 첫 번째 값에 0을 할당하고, 그 다음 값부터 1씩 증가시킵니다. 즉, 내부적으로 Up은 0, Down은 1을 의미합니다.</p>
<h3 id="void-타입">void 타입</h3>
<pre><code class="language-ts">funtion logMessage(msg: string):void {
  console.log(msg)
}</code></pre>
<p>void는 함수가 아무값도 반환하지 않을때 사용
보통 로그 출력, 이벤트 처리 등 부수효과를 위한 작업에 사용합니다 <strong>not undefined</strong></p>
<h3 id="never-타입">never 타입</h3>
<pre><code class="language-ts">function throwError(): never {
  throw new Error(&quot;예외발생&quot;)
}
function infiniteLoof(): never {
  while (true) {}
}</code></pre>
<p>절대 끝나지 않거나 절대 반환되지 않은 함수  </p>
<h3 id="unknown-타입">unknown 타입</h3>
<pre><code class="language-ts">let input: unkoum = &quot;hello&quot;

input.toUpperCase() &lt;&lt;&lt;&lt;&lt; 에러발생

if ( typeof input === string ) {
  console.log(input.upperCase()) // 실행
]</code></pre>
<p>unknown은 불확실한 상황에서 사용가능, 하지만 사용할때 타입을 지정해야지만 사용할 수 있어 any보다 안전합니다.</p>
<h3 id="유니언-타입">유니언 타입</h3>
<pre><code class="language-ts">let status: &quot;error&quot; | &quot;success&quot;
status = &quot;error&quot; // 값이 있으니 성공 O
status = &quot;min&quot; // 값이 없으니 실패 X

function printId (id: number | string){
  if(typeof number) {
    console.log(&#39;숫자&quot;)
  } else {
    console.log(&quot;문자&quot;)
  }
}</code></pre>
<p>값이 하나 이상일때 표기함, 선언한 값만 사용할 수 있습니다.
<code>printId</code>처럼 사용가능합니다. 타입에 따라 분기 처리하는 로직을 <strong>타입가드</strong>라고 합니다.</p>
<h3 id="인터섹션-타입intersection">인터섹션 타입(intersection)</h3>
<pre><code class="language-ts">type HasName = { name: string }
type HasAge = { age: number }

type People = HasName &amp; HasAge
const People: People = {
  name: &quot;이름&quot;
  age: 35
}</code></pre>
<p>여러 타입을 동시에 만족할때 사용합니다, A타입과 B타입의 결합입니다.</p>
<p>|구분|기본타입|고급타입</p>
<p>구분|기본 타입|고급 타입
|---|---|---|
|설명|단일 데이터 값을 표현하는 타입 | 여러 타입을 조합하거나 구조화된 데이터를 표현|
|정의 방식|값의 타입을 명확히 지정| 타입을 결합하거나 확장하여 새 타입 생성|
|적용 범위|JavaScript에서 기본적으로 제공되는 타입| 타입스크립트에서 추가된 타입 시스템 기능
| 예시 | string, number ... | union, tuple ... |</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터3 - 타입스크립트 개요, 타입스크립트 도입]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B0-3-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EC%9A%94-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%8F%84%EC%9E%85</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B0-3-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EC%9A%94-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%8F%84%EC%9E%85</guid>
            <pubDate>Tue, 09 Jun 2026 01:52:40 GMT</pubDate>
            <description><![CDATA[<h1 id="👀-타입스크립트-개요">👀 타입스크립트 개요</h1>
<p>타입스크립트는 69.5% 사용자 만족도와 33.8%의 학습 선호도를 기록하며 상위권에 있는 언어입니다. 
이러한 평가중 한가지 특징은 타입스크립트가 자바스크립트의 <strong>상위 집합(superset)</strong>이라는 구조적 특징때문입니다. 
이러한 특성때문에 별도의 학습없이 점진적으로 도입이 가능하고 끝내 완전한 마이그레이션으로 이어지게 됩니다.</p>
<p>장점 - 정적 타입 시스템을 기반으로 한 <strong>오류방지</strong>입니다. 코드작업에서 타입 에러를 잡아낼 수 있어 런타임 오류 발생을 현저히 낮춰줍니다. 그리고 개발 생상성을 향상시키고 협업구조 잡는 등 여러 편의기능을 제공합니다.</p>
<h3 id="타입스크립트-핵심-개념-3가지">타입스크립트 핵심 개념 3가지</h3>
<ul>
<li><strong>Superset of JavaScript</strong>: 자바스크립트 문법과 100% 호환됩니다.</li>
<li><strong>Type System</strong>&quot;: 변수, 함수, 객체 등에 타입을 명시하여 정적 분석과 자동완성을 지원합니다.</li>
<li><strong>Clean Output</strong>: 타입스크립트는 컴파일을 할 시 타입스크립트에서 완벽한 자바스크립트 변환해줍니다.</li>
</ul>
<blockquote>
<p>이는타입스크립트가 런타임 환경에 어떠한 영향을 주지 않고, 오직 개발 시점에서만 개입하는 언어입니다.</p>
</blockquote>
<h3 id="다양한-자바스크립트-버전으로">다양한 자바스크립트 버전으로</h3>
<p> 타입스크립트는 다양한 ECMAScript(ES) 버전으로 컴파일을 지원합니다. tsconfig.jons을 통해 특정 자바스크립트 버전으로 코드를 변환할 수 있습니다.</p>
<p> 단 타입스크립트는 런타임 기능(polyfill)을 포함하지 않습니다. Promise, Array, prototype.includes같은 ES6이후 도입된 내장 객체나 메서드는 타입스크립트 자체에서 지원하지않으며 단지 타입만 검사해줍니다. 그래서 Babel을 함께 사용하여 런타임 환경 호환성과 최신 자바스크립트 기능에 대한 폴리필을 적용하는 방식이 널리 사용됩니다.</p>
<h1 id="🔨-타입스크립트-도입">🔨 타입스크립트 도입</h1>
<p> 도입전 판단 기준은 해당 기술이 실질적으로 어떤 생산성과 안정성, 협업 효율성을 제공하는지 판단해야합니다.</p>
<h2 id="타입스크립트를-사용해야-하는-이유">타입스크립트를 사용해야 하는 이유</h2>
<h3 id="1-정적-타입-검사로-인한-오류-예방">1. 정적 타입 검사로 인한 오류 예방</h3>
<p> 코드 작성 시점에 타입 오류를 잡아내어, 런타임에서 발생할 수 있는 예기치 않은 오류를 방지할 수 있습니다.
 그리고 팀협업에서도 오해를 줄여 의사소통 비용을 줄이는데 매우 효과적입니다.</p>
<h3 id="2-개발자-도구와의-강력한-통합">2. 개발자 도구와의 강력한 통합</h3>
<p> IDE(통합개발환경 - 컴파일, 디버그, 배포 등)와 시너지를 극대화 합니다. 타입을 명시하면 코드에서 자동 완성 기능을 제공하는 등 해당 기능을 통해 객체 속성에대한 명확한 정보와 타입을 쉽게 확인할 수 있으며, 오류 방지에 도움을 줍니다.</p>
<h3 id="3-jsdoc과-타입스크립트의-통합">3. JSDoc과 타입스크립트의 통합</h3>
<p> <img src="https://velog.velcdn.com/images/songha-5/post/2f5969cc-cf9e-442d-b0a4-791ee8af2fbd/image.png" alt="">
JSDoc은 코드위에 주석으로 어떤 타입인지, 어떻게 사용해야하는지 적어놓는 주석입니다.</p>
<p>코드를 작성할 때 어떤 타입인지, 어떻게 사용해야하는지 알려주어 TypeScript와 비슷해보이지만, 해당 코드는 빌드때 에러를 뱉기 때문에 개발환경에서 바로 알 수 있는 TypeScript와는 다릅니다.</p>
<p>그래서 타입은 TypeScript에게, 설명은 JSDoc에게 맡기는것이 실무에서 안정성과 가독성을 모두 확보하는 길입니다.</p>
<h3 id="4-라이브러리와의-시너지">4. 라이브러리와의 시너지</h3>
<p>현재 개발은 많은 라이브러리와 함께합니다. 예전에는 라이브러리를 사용하기 위해서 하나하나 사용방법을 읽어봐야했지만 TypeScript가 적용되며 상황은 달라졌습니다.
개발에서 자동 완성을 통해 허용 가능한 값의 범위를 바로 확인할 수 있어졌습니다.</p>
<p>하지만 TypeScript를 지원하지 않는 경우도 있습니다. 이 문제를 해결하기 위해 <strong>DefinitetyType 프로젝트</strong>가 등장했습니다. 전세계 개발자들이 자발적으로 작성하고 유지보수하는 타입 정의 모음입니다.
TypeScript지원을 하지않는 라이브러리도 @type을 설치하면 TypeScript를 사용 할 수 있도록 하였습니다. 예시로 @type/react-hook-form이 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터2 자바스크립트 - part 3]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B02-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-part-3</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B02-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-part-3</guid>
            <pubDate>Thu, 04 Jun 2026 06:20:55 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-비동기와-싱글-스레드">✨ 비동기와 싱글 스레드</h1>
<p>자바스크립트는 싱글 스레드이기 때문에 작업이 오래걸리는 작업이 메인스레드를 차단하지 않도록 비동기처리를 위한 다양한 방법을 제공합니다.</p>
<h2 id="자바스크립트의-싱글-스레드와-비동기-처리">자바스크립트의 싱글 스레드와 비동기 처리</h2>
<p><strong>싱글 스레드</strong>: 한번에 한가지 일만 처리할 수 있음, 호출스택에 의해 순차적으로 실행</p>
<blockquote>
<p>병목현상이 일어남, 그래서 이벤트 루프를 활용하여 해당 문제를 해결하였습니다.</p>
</blockquote>
<p>하지만 어째서 싱글스레드인데 멀티스레드인것처럼 데이터를 불러오면서 동시에 다른일을 처리할 수 있을까?</p>
<blockquote>
<p>JS는 싱글스레드이지만 JS의 환경(브라우저, Node.js)은 멀티스레드이기 때문입니다. 
특수한 기능을 가진 API들(fetce, setTimeout 등)을 만나면 브라우저나 node.js에게 넘기고 다른 작업을하고있습니다. 그러다가 브라우저나 node.js에서 해당 작업이 끝나면 다시 대기열(Task Queue)에 보냅니다. 이후 이벤트루프가 기존 작업이 모두 끝나 콜 스택이 비면, 대기열에 있던 작업들을 콜 스택으로 넘겨 실행하게됩니다.</p>
</blockquote>
<h3 id="이벤트-루프event-loop와-비동기-처리">이벤트 루프(event loop)와 비동기 처리</h3>
<p><img src="https://velog.velcdn.com/images/songha-5/post/d79a0df0-ebc0-4924-8546-d6a8ec53b0eb/image.png" alt=""></p>
<p>이벤트 루프는 자바스크립트 엔진이 비동기 작업을 처리하는 방식입니다. 백그라운드에서 비동기 작업이 완료되면 그 결과를 콜백큐(callback Queue)에 넣고 메인 스레드가 비어있을 때 그 콜백을 실행합니다.</p>
<ol>
<li><strong>동기 함수 실행</strong>: 콜스택(LIFO-후입선출)에 작업할 내용들이 들어가며 순차적으로 실행됩니다.</li>
<li><strong>비동기 작업 처리</strong>: setTimeout등 비동기 작업이 있으면 Web API로 보냅니다. 브라우저나 node.js가 해당작업을 수행합니다.</li>
<li><strong>콜백큐</strong>: 선입선출, Web API에서 비동기 작업이 완료되면 콜백큐로 넘어갑니다.</li>
<li><strong>이벤트루프</strong>: 콜 스택이 비어있는지 확인 후 비어있다면 콜백큐작업을 콜스택으로 이동시킵니다.</li>
<li><strong>콜백 실행</strong>: 콜백 함수가 실행됩니다<pre><code>console.log(&quot;1. 기존 작업 시작&quot;)
</code></pre></li>
</ol>
<p>setTimeout(() =&gt; {
  console.log(&quot;2. 일반 대기열 (Task Queue)&quot;); 
}, 0)</p>
<p>Promise.resolve().then(() =&gt; {
  console.log(&quot;3. VIP 대기열 (Microtask Queue)&quot;)
});</p>
<p>console.log(&quot;4. 기존 작업 끝&quot;)</p>
<pre><code>- 순서 1,4,3,2

### 동기 작업이 오래 걸릴 때의 문제점
하나의 동기작업이 오래걸리면 그 작업이 끝날동안 다른작업은 하지 못하게 됩니다. 이를 **블로킹(blocking)**이라 합니다.
</code></pre><p>console.log(&quot;1. 커피 주문 받기&quot;);</p>
<p>// 블로킹 코드
let sum = 0;
for (let i = 0; i &lt; 10000000000; i++) {
  sum += i; 
}
console.log(&quot;2. 100억 번 연산 끝: &quot;, sum);</p>
<p>// 위의 100억 번 연산이 끝날 때까지 절대 실행되지 않음
console.log(&quot;3. 다음 손님 주문 받기&quot;);</p>
<pre><code>**블로킹 코드**: 콜 스택(메인 스레드)을 너무 오래 차지하여 브라우저 화면을 멈추게 만드는 무거운 동기 코드.
</code></pre><p>//해결책 1</p>
<p>console.log(&quot;1. 데이터 요청 (주문 받기)&quot;)</p>
<p>// web API 변경
fetch(&#39;<a href="https://api.example.com/data&#39;">https://api.example.com/data&#39;</a>)
  .then(response =&gt; response.json())
  .then(data =&gt; console.log(&quot;3. 데이터 도착!&quot;, data))</p>
<p>// 아래코드 실행
console.log(&quot;2. 화면 그리기 계속 (다음 손님 받기)&quot;)</p>
<pre><code>위는 해결책 1입니다.</code></pre><p>// 해결책 2
// 메인 매장으로부터 일거리를 받습니다.
self.onmessage = function(event) {
  if (event.data === &#39;start_calculation&#39;) {</p>
<pre><code>// 연산을 메인 스레드 대신 여기서 처리합니다
let sum = 0;
for (let i = 0; i &lt; 10000000000; i++) {
  sum += i; 
}

// 계산이 다 끝나면 메인으로 결과를 전송합니다.
self.postMessage(sum)</code></pre><p>  }
}</p>
<p>//실제 코드실행
console.log(&quot;1. 메인 매장 오픈&quot;);</p>
<p>// 1. Web Worker 생성
const myWorker = new Worker(&#39;worker.js&#39;);</p>
<p>// 2. Web Worker 작업 전달
myWorker.postMessage(&#39;start_calculation&#39;);</p>
<p>// 3. 결과 받음
myWorker.onmessage = function(event) {
  console.log(&quot;3.계산 완료함 결과: &quot;, event.data);
};</p>
<p>// 아래코드는 블로킹없이 실행
console.log(&quot;2. 커피만들기&quot;);</p>
<pre><code>해결책2 입니다.

1. 해결책 1 (비동기 API): 통신이나 대기가 필요한 일은 fetch, setTimeout 같은 Web API에 넘겨서 해결합니다.

2. 해결책 2 (Web Worker): 브라우저 자체의 연산력(CPU)을 엄청나게 써야 하는 복잡한 계산은, 메인 스레드를 막지 않도록 Web Worker를 생성하여 아예 다른 스레드(뒷방 직원)로 계산을 분산시켜 해결합니다.

## 콜백, 프로미스, async/await
비동기 작업을 처리하는 방식은 세가지가 있습니다. 콜백으로 시작하여 코드 유지보수성을 보완하기위해 프로미스가 나왔고, 거기에서 더 발전한 async/await가 나왔습니다.

이 세가지 방법중에 상황에 맞게 사용해야합니다.

### 콜백 함수 - 비동기의 시작
작업이 끝난 후 실행될 함수를 인자로 넘기는 방식입니다.
당장 실행하지 않고 어떤 함수한테 특정 상황이 되면 실행해주는 함수</code></pre><p>function 브라우저야_이거_실행해줘(콜백함수) {
  // 어떤 작업이 끝남
  콜백함수() // 남이 나를 호출함 (Call Back)
}</p>
<pre><code>대표적인 콜백함수
**이벤트 처리**: 버튼을 &#39;클릭했을 때&#39; 실행 (onClick={() =&gt; {}})
**배열 가공**: 배열의 요소를 &#39;하나씩 꺼낼 때마다&#39; 실행 (map((item) =&gt; {}))
**비동기 처리**: 데이터가 &#39;서버에서 도착했을 때&#39; 실행 (then((data) =&gt; {}))

&gt; 똑같이 생긴 함수도 어떻게 쓰이냐에 따라 일반 함수가 될 수도, 콜백 함수가 될 수도 있습니다. 호출주제가 개발자이면 일반함수, 호출주제가 이벤트(브라우저)이면 콜백함수 입니다.
</code></pre><p>const fruits = [&#39;사과&#39;, &#39;바나나&#39;, &#39;딸기&#39;];</p>
<p>// forEach가 한번 돌때마다 이벤트(브라우저)가 계산시작
fruits.forEach((fruit) =&gt; {
  console.log(<code>${fruit} 맛있겠다!</code>); 
});</p>
<pre><code>
콜백을 너무 겹쳐서 사용하면 코드관리가 힘들어지는 **콜백지옥**이 발생할 수 있습니다.
</code></pre><p>//A받아와서 B를 실행하고 그거로 C를 실행하고 그 결과를 넘겨서 D를 실행해주는 코드
getDataA(function(a) {
  getDataB(a, function(b) {
    getDataC(b, function(c) {
      getDataD(c, function(d) {
        console.log(&quot;콜백지옥&quot;)
      })
    })
  })
})</p>
<pre><code>
### 프로미스 (Promise) - 비동기를 직선으로
프로미스는 작업의 결과를 하나의 객체로감싸는 방식입니다.</code></pre><p>// 한줄로 정리할 수 있습니다.
주문하기()
  .then((커피) =&gt; 마시기(커피))
  .then((빈컵) =&gt; 버리기(빈컵))
  .then(() =&gt; console.log(&quot;다 마셨음&quot;))
  .catch((에러) =&gt; console.log(&quot;문제 발생: &quot;, 에러)); // 중간에 에러가 나도 여기서 잡아냄</p>
<pre><code>
### async/await
프로미스를 사용할 때 너무 한줄로 이루어져있어 불편한 점이 있었습니다. 그래서 비동기를 동기코드처럼 읽도록 바꾸기 위해 async/await가 나왔습니다.</code></pre><p>async function 커피마시기() {
  try {
    // Promise앞에 await를 넣어서 작업이 끝날때까지 대기시킵니다.
    const 커피 = await 주문하기()</p>
<pre><code>// 위 줄에서 작업을 끝내면 아래 코드가 실행됩니다. (그전에는 실행이 되지 않습니다.
console.log(`${커피} 나왔습니다!`)
await 마시기(커피)
console.log(&quot;다 마셨다!&quot;)</code></pre><p>  } catch (에러) {
    // try-catch문으로 에러를 잡아냅니다.
    console.log(&quot;문제 발생: &quot;, 에러)
  }
}</p>
<p>// async/await를 한번에 데이터 불러오기</p>
<blockquote>
<p>순차적으로 실행 (비효율적)
const a = await fetchA()
const aa = await fetchB()
const aaa = await fetchC()</p>
</blockquote>
<blockquote>
<p>병렬로 실행
const [a, b, c] = await Promise.all([fetchA, fetchB, fetchC])</p>
</blockquote>
<pre><code>
### 비동기 사용시 주의사항
1. forEach는 비동기를 기다리지 않습니다.
2. 직렬작업과 병렬작업을 적절하게 사용 (단 모든작업이 반드시 성공해야함)
3. promise와 async/await혼용금지 

## 모듈 시스템과 import/export
하나의 긴 코드 덩어리를 작성하면 유지보수와 코드의 재사용성이 낮습니다. 모듈화를 통해 분리하여 관리하면 유지보수와 코드의 재사용성이 올라갈 것 입니다.

## 기존방식 - CommonJS(require/module.export)
webpack, babel같은 번들러 설정이 반드시 필요합니다.
- require()로 모듈을 가져오고
- module.exports로 내보냅니다.</code></pre><p>// math.js
const add = (a, b) =&gt; a + b;
const multiply = (a, b) =&gt; a * b;</p>
<p>// module.exports 내보내기
module.exports = {
  add,
  multiply
}</p>
<p>// app.js
// require 불러오기 (경로 표기 필수)
const math = require(&#39;./math&#39;)</p>
<p>console.log(math.add(2, 3))      // 출력: 5
console.log(math.multiply(4, 5)) // 출력: 20</p>
<pre><code>
## export 방식</code></pre><p>// 변수나 함수 앞에 바로 export를 붙입니다.
export const add = (a, b) =&gt; a + b
export const multiply = (a, b) =&gt; a * b</p>
<p>// 이름을 정확히 지정해서 중괄호 { } 안에 넣어 불러와야 합니다
import { add, multiply } from &#39;./math.js&#39;</p>
<p>console.log(add(2, 3));       // 5
console.log(multiply(4, 5));  // 20</p>
<pre><code>하나만 불러오는 방식도 있습니다.</code></pre><p>const user = {
  name: &#39;Alice&#39;,
  age: 25
};</p>
<p>// user를 내보냅니다.
export default user</p>
<hr>
<p>// 하나만 넘어왔기 때문에 중괄호 { } 가 필요 없습니다
// 이름을 바꿔서 부를 수도 있습니다
import myUser from &#39;./user.js&#39;</p>
<p>console.log(myUser.name); // &#39;Alice&#39;</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[챕터2 자바스크립트 - part 2]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B02-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B02-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-2</guid>
            <pubDate>Thu, 04 Jun 2026 02:15:51 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-객체와-배열">💻 객체와 배열</h1>
<p>키-값 쌍으로 정보를 표현합니다.</p>
<h2 id="객체object">객체(Object)</h2>
<pre><code>const person = {
    name: &#39;john&#39;,
    job: &#39;Developer&#39;,
    age: 30
}</code></pre><p>중괄호안에 key:value형태로 속성을 정의하는 구조입니다.</p>
<h3 id="1-객체의-기본-메서드">1. 객체의 기본 메서드</h3>
<p>메서드란: 객체의 속성을 쉽게 관리하고 조작하는데 사용 객체의 프로퍼티로 할당된 함수입니다.</p>
<pre><code>console.log(person.name) // john
console.log(person[job]) // Developer

person.age = 40 // 값수정
person[age] = 34 // 값수정

deledt person.age // 삭제</code></pre><p><strong>속성 접근과 수정, 삭제</strong> : 객체의 속성(프로퍼티)에 접근할때 점표기법과 대괄호 표기법을 사용할 수 있습니다.</p>
<h2 id="객체의-es6">객체의 ES6+</h2>
<p>더 간결하고 직관적으로 변경되었습니다.</p>
<pre><code>const { name, job } = person
console.log(name) // john</code></pre><p><strong>객체 구조 분해 할당</strong>: 객체의 속성을 쉽게 분리할 수 있습니다.</p>
<pre><code>const name = &#39;alice&#39;
const age = 24
// 사용
const user = { name, age }

// 미사용
const user = new Object()
user.name = name
user.age = age</code></pre><p><strong>객체 리터널</strong>: 속성이름과 변수이름이 같으면 축약가능합니다.</p>
<pre><code>const obj1 = { a:1, b:2 }
const obj2 = { b:3, c:4 }

const mergedObj = { ...obj1, ...obj2 }
console.log(mergedObj) // {a:1, b:3, c:4 }</code></pre><p><strong>객체 병합(스프레드 연산자)</strong>: 여러객체를 하나로 병합살 수 있습니다, 중복되는 값은 뒤에 넣은 값으로 대체됩니다.</p>
<h2 id="배열array">배열(Array)</h2>
<p>대괄호 안의 순서대로 나열된 값들의 리스트입니다.</p>
<pre><code>const fruits = [&#39;Apple&#39;, &#39;Banana&#39;, &#39;Cherry&#39;]</code></pre><h3 id="배열의-기본-메서드">배열의 기본 메서드</h3>
<pre><code>console.log(fruits[0]) // Apple</code></pre><p><strong>배열 요소 접근</strong>:인덱스를 사용해 접근 가능합니다. 0번부터 시작합니다</p>
<pre><code>fruits[1] = &quot;Blueberry&quot;
fruits.push = &quot;Mango&quot;
fruits.pop //마지막 요소 삭제
fruits.splice(1,1) //1번요소 1개 삭제</code></pre><p><strong>배열 수정/추가/삭제</strong>: 요소를 추가,수정,삭제를할 수 있습니다.</p>
<h2 id="배열의-es6">배열의 ES6+</h2>
<pre><code>const [first, second] = fruits
const combined = [...fruits, &quot;mango&quot;]</code></pre><p><strong>배열 구조 분해 할당</strong>: 배열의 요소에 변수를 쉽게 할당합니다.</p>
<pre><code>const user = {
    {name: &quot;alice&quot;, age: 28 },
    {name: &quot;bob&quot;, age: 32 },
    {name: &quot;charlie&quot;, age: 42 }
}

1. forEach = 요소 하나하나에 대해 로그 출력
user.forEach((user) =&gt; {
    console.log(`${user.name}의 ${user.age}는?)
})

2. map = 이름만 추출한 새 배열만들기
const name = user.map((user) =&gt; user.name))
console.log(name) // &quot;alice&quot;,&quot;bob&quot;, &quot;charlie&quot;

3. filter = 필터링
const adult = user.filter((user) =&gt; user.age &gt;= 30)
console.log(adult) 
// {
//    {name: &quot;bob&quot;, age: 32}
//    {name: &quot;charlie&quot;, age: 42}
// }</code></pre><p><strong>forEach</strong>: 배열을 순회하여 부수효과(side Effect)를 줄 때 사용, 반환값 없음
<strong>map</strong>: 배열을 변형하여 값을 반환
<strong>fllter</strong>: 배열에서 특정 조건을 만족한것들만 반환</p>
<h4 id="이터러블객체와-이터레이터-객체">이터러블객체와 이터레이터 객체</h4>
<pre><code>const fruits = [&#39;🍎&#39;, &#39;🍌&#39;]; // 1. 배열은 기본적으로 &#39;이터러블&#39; 객체입니다.

// 2. 이터러블 객체에서 숨겨진 함수를 실행해 &#39;이터레이터&#39;를 뽑아냅니다.
const iterator = fruits[Symbol.iterator](); 

// 3. 이터레이터의 next() 메서드를 호출해 봅시다.
console.log(iterator.next()); 
// 출력: { value: &#39;🍎&#39;, done: false }

console.log(iterator.next()); 
// 출력: { value: &#39;🍌&#39;, done: false }

console.log(iterator.next()); 
// 출력: { value: undefined, done: true }</code></pre><p><strong>이터너블 객체(iterable Object)</strong>
반복(순회)가능한 객체라는 뜻입니다. map이나 forEach, 배열, 객체 등이 있습니다.</p>
<p><strong>이터레이터 객체(iterator Object)</strong>
이터레이터는 이터러블 객체에서 실제로 데이터를 하나씩 뽑아줍니다.</p>
<ul>
<li>이터레이터는 반드시 next()를 가지고 있어야합니다</li>
<li>next()를 호출할 때마다 뽑아낸 값(value)과 순회가 다끝났는지 알려주는 done을 뱉어냅니다.</li>
</ul>
<h2 id="객체와-배열의-복사">객체와 배열의 복사</h2>
<p>객체와 배열은 참조 타입입니다. 데이터가 저장된 메모리 주소를 복사합니다.</p>
<pre><code>const original = { name: &#39;alice&#39; }
const copy = original

copy.name = &#39;jin&#39;
console.log(original.name) // jin</code></pre><p>이처럼 copy를 바꾸어도 original까지 바뀌는 모습을 볼 수 있었습니다. 이처럼 참조복사의 값 변경은 유의해야합니다.</p>
<h3 id="1-얕은-복사">1. 얕은 복사</h3>
<p>중첩된 객체나 배열은 여전히 같은 참조를 공유하는 복사 방식입니다. 
Object.assign()이나 스프레드 연산자가 있습니다.</p>
<pre><code>const [user, setUser] = useState({ name: &#39;alice&#39;, age: 24 });

// 주소가 그대로라고 인식 화면이 안바뀜 (react)
user.age = 25;
setUser(user); 

// 얕은 복사 - 화면 바뀜 (react)
setUser({ ...user, age: 25 }); 
// 새로운 { } 껍데기가 왔다고 인식, 주소가 바뀌었다고 인식하여 화면이 바뀜</code></pre><h3 id="2-깊은-복사">2. 깊은 복사</h3>
<p>중첩된 객체나 배열까지 새로운 참조로 복사하는 방식입니다. 완전히 독립되어있으므로 원본값을 보호할 수 있습니다.
해당 방법은 CPU부담이 큽니다.</p>
<pre><code>//추천하는 방식
const deepCopied = structuredClone(user)

//문자열로 바꾸었다가 다시 객체로 조립
const deepCopied = JSON.parse(JSON.stringify(original))

// 외부 라이브러리 
import _ from &#39;lodash&#39;
const deepCopied = _.cloneDeep(original)</code></pre><table>
<thead>
<tr>
<th>구분</th>
<th>얕은 복사(Shallow Copy)</th>
<th>깊은 복사 (Deep Copy)</th>
</tr>
</thead>
<tbody><tr>
<td>핵심 개념</td>
<td>가장 바깥쪽 껍데기만 새로 만듦</td>
<td>껍데기부터 안쪽 내용물까지 완전히 새로 찍어냄</td>
</tr>
<tr>
<td>중첩 객체(내부 데이터)</td>
<td>원본과 메모리 주소(참조)를 공유함</td>
<td>원본과 완벽하게 분리된 새로운 메모리를 할당함</td>
</tr>
<tr>
<td>중첩 객체 수정 시</td>
<td>원본 데이터도 함께 변경됨 (주의!)</td>
<td>원본 데이터에 전혀 영향을 주지 않음</td>
</tr>
<tr>
<td>대표적인 사용 문법</td>
<td>전개 구문 (...), Object.assign(), slice()</td>
<td>structuredClone(), lodash.cloneDeep(), JSON 변환</td>
</tr>
<tr>
<td>성능 (CPU/메모리)</td>
<td>가볍고 매우 빠름 (주소만 복사하니까)</td>
<td>무겁고 느림 (바닥까지 다 복사해야 하니까)</td>
</tr>
<tr>
<td>리액트(React) 실무</td>
<td>기본값 (상태 업데이트할 때 99% 사용)</td>
<td>아주 깊게 중첩된 복잡한 상태를 다룰 때만 예외적 사용</td>
</tr>
</tbody></table>
<h1 id="🔥-클래스와-프로토타입">🔥 클래스와 프로토타입</h1>
<p>자바스크립트는 프로토타입 기반 객체지향 언어로, ES6이후 class문법으로 더 직관적으로 사용할 수 있습니다.</p>
<h2 id="문법적-설탕">문법적 설탕</h2>
<p>기존 프로토타입 기반 객체지향 언어를 클래스 기반 객체지향처럼 보이게 겉모습만 바꾼모습이다</p>
<pre><code>// 현재의 방식 (ES6 class 문법)
class Person {
  // 데이터 세팅
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  // 행동(메서드) 정의를 클래스 안에 깔끔하게 모음!
  sayHello() {
    console.log(`안녕하세요, 저는 ${this.name}입니다.`);
  }
}

const user1 = new Person(&#39;alice&#39;, 24);
user1.sayHello();</code></pre><h3 id="1-es6-이전의-프로토타입-방식">1. ES6 이전의 프로토타입 방식</h3>
<pre><code>// 과거의 방식 (프로토타입 직접 다루기)
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 메서드를 추가하려면 prototype에 따로 붙여줘야 함
Person.prototype.sayHello = function() {
  console.log(&quot;안녕하세요, 저는 &quot; + this.name + &quot;입니다.&quot;);
};

const user1 = new Person(&#39;alice&#39;, 24);
user1.sayHello();</code></pre><p>프로토타입은 모든 코드가 분리되어있고 코드가 한눈에 보이지 않아 클래스로 작성하는것보다 불편했습니다.</p>
<h2 id="프로토타입-체인">프로토타입 체인</h2>
<p>프로토타입 체인은 객체가 특정 속성이나 메서드를 가지고 있지 않다면 <code>프로토타입 체이닝</code>을 작동합니다. 바로 그 위의 부모코드에서 찾는 행위입니다. 마치 유산상속과도 같습니다.</p>
<pre><code>// 1. 1세대 (조부모)
const grandparent = { house: &#39;기와집&#39; }

// 2. 2세대 (부모) 
const parent = Object.create(grandparent); 
parent.car = &#39;소나타&#39;

// 3. 3세대 (나)
const me = Object.create(parent); 
me.bike = &#39;자전거&#39;

console.log(me.bike);  
// 출력: &#39;자전거&#39;

console.log(me.car);   
// 출력: &#39;소나타&#39; - 나는 없지만, 체인을 타고 올라가 car를 찾음

console.log(me.house); 
// 출력: &#39;기와집&#39; - 나는 없지만, 체인을 타고 올라가 house를 찾음

console.log(me.airplane); 
// 출력: undefined - 받아올 부모가 없음</code></pre><h2 id="캡슐화-gettersetter와-private-field">캡슐화: getter/setter와 private field</h2>
<p>객체의 내부 정보를 보호하기 위해 <code>getter, setter, private field</code>를 사용해 <code>캡슐화</code>를 할 수 있습니다.</p>
<pre><code>class BankAccount {
  // 1. Private Field (#): 외부에서 절대 직접 데이터 변경이 불가능
  #balance = 0; 

  constructor(accountHolder) {
    this.accountHolder = accountHolder
  }

  // 2. Getter (get) - 조회
  get balance() {
    return `${this.#balance}원`
  }

  // 3. Setter (set): - 수정
  set balance(amount) {
    // 캡슐화: 엉뚱한 값이 들어오는 것을 사전에 차단
    if (amount &lt; 0) {
      console.log(&quot;오류: 통장 잔고는 마이너스가 될 수 없습니다!&quot;);
      return
    }
    this.#balance = amount
    console.log(`${amount}원이 정상적으로 설정되었습니다.`)
  }
}

const myAccount = new BankAccount(&#39;alice&#39;);

// 직접 접근 &gt; 에러
console.log(myAccount.#balance)

// Setter 접근
myAccount.balance = 10000 // 출력: 10000원이 정상적으로 설정되었습니다.

// Getter 조회
console.log(myAccount.balance) // 출력: 10000원</code></pre><p>컨벤션으로 변수나 함수앞에 _(언더스코어)가 붙으면 내부구현으로 간주했습니다.하지만 프라이빗 필드(ES2022)가 지원되면서 레거시 코드에서만 찾아 볼 수 있게 되었습니다.</p>
<h2 id="상속-vs-조합-is-a-vs-has-a">상속 vs 조합 (Is-a vs Has-a)</h2>
<h4 id="조합is-a">조합(Is-a)</h4>
<p>상속을 했을 때 무조건 좋은것은 아닙니다.
자식 클래스가 부모 클래스의 기능 중 딱 &#39;하나&#39;만 필요해서 상속을 받았는데, 전혀 필요 없는 수십 개의 다른 기능과 데이터까지 강제로 전부 물려받아 객체가 쓸데없이 무거워진다는 단점이 있습니다.</p>
<ol>
<li><p><strong>너무 강력하게 묶여있음</strong>
부모 클래스가 바뀐다면 그 아래 연결해둔 자식 클래스에게 전부 영향이 갑니다. 그래서 멀쩡한 자식클래스에게 에러가나는 경우가 생깁니다.</p>
</li>
<li><p><strong>다중 조합이 불가능함</strong>
하나의 클래스만 조합이 가능합니다. 두개를 연결해 쓸 수 없습니다.</p>
</li>
<li><p><strong>캡슐화가 깨짐</strong>
기껏 만들어 놓은 캡슐화가 깨집니다. 자식클래스가 부모클래스를 사용하려면 부모클래스를 다 알아야지 사용할 수 있습니다.</p>
</li>
</ol>
<h4 id="조합has-a">조합(has-a)</h4>
<p>독립적인 기능들을 작은 단위로 만들어 필요할 때마다 부품처럼 끼워 맞추는 &#39;컴포지션(Composition, 조합)&#39;을 추천합니다.</p>
<pre><code>const driveBehavior = (state) =&gt; ({
  drive: () =&gt; console.log(`${state.name}이(가) 땅을 달립니다.`)
})

const flyBehavior = (state) =&gt; ({
  fly: () =&gt; console.log(`${state.name}이(가) 하늘을 납니다.`)
})


const createAirplane = (name) =&gt; {
  const state = { name }

  // 조립
  return Object.assign(
    {}, 
    driveBehavior(state), 
    flyBehavior(state)
  )

  // or
  return {
    ...driveBehavior(state),
    ...flyBehavior(state)
  };
}

const myJet = createAirplane(&quot;보잉 747&quot;)
myJet.drive() // 보잉 747이(가) 땅을 달립니다.
myJet.fly()   // 보잉 747이(가) 하늘을 납니다.</code></pre><p>클래스 상속보다 더 유연하게 사용할 수 있습니다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>상속</th>
<th>조합</th>
</tr>
</thead>
<tbody><tr>
<td>결합도</td>
<td>매우 강함 (수직적 결합)</td>
<td>약함 (수평적 결합)</td>
</tr>
<tr>
<td>코드 재사용 방식</td>
<td>부모 클래스의 내부 구현을 통째로 물려받음 (원치 않는 기능도 강제로 받음)</td>
<td>필요한 기능(부품)만 골라서 내 객체 안에 조립하여 사용함</td>
</tr>
<tr>
<td>다중 상속</td>
<td>불가능</td>
<td>무한대</td>
</tr>
<tr>
<td>유연성 및 확장성</td>
<td>낮음</td>
<td>매우 높음</td>
</tr>
<tr>
<td>리액트 실무</td>
<td>공식 문서에서 사용을 피하라고 권고함</td>
<td>컴포넌트 합성에 적극 권장됨</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[챕터2 자바스크립트 - part 1]]></title>
            <link>https://velog.io/@songha-5/2%EC%9E%A5-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1</link>
            <guid>https://velog.io/@songha-5/2%EC%9E%A5-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-1</guid>
            <pubDate>Mon, 01 Jun 2026 02:38:38 GMT</pubDate>
            <description><![CDATA[<h1 id="자바스크립트의-역할">자바스크립트의 역할</h1>
<p>원래 웹페이지에 간단한 인터렉트를 주기위한 언어였지만, 지금은 웹의 핵심기술로 확장되었습니다.</p>
<ol>
<li><strong>웹 프론트엔드</strong>: HTML요소 조작, 사용자 이벤트 처리, 상태 변화에 따른 화면 렌더링</li>
<li><strong>웹 백엔드</strong>: Node.js 기반으로 서버 개발, API 구축, 실시간 처리</li>
<li><strong>모바일 앱 개발</strong>: Electron, Tauri 등을 통해 Slact, VSCode와 같은 데스크톱 앱 개발</li>
<li><strong>테스트 및 자동화</strong>: Playwright, Puppeteer로 브라우저 자동화, Jest로 단위 테스트</li>
<li><strong>디자인 도구/AI 툴 연동</strong>: Figma Plugin, AI Code Assistant등에서 JS로 기능확장</li>
</ol>
<h1 id="자바스크립트의-현황">자바스크립트의 현황</h1>
<p>Node.js 등장 이후 프론트엔드와 백엔드를 모두 개발할 수 있게 되었습니다. Node.js는 자바스크립트가 브라우저 작동에서 서버 환경에서 실행 할 수 있게 만들어주었습니다.</p>
<h2 id="1-대기업은-자바를-스타트업은-nodejs">1. 대기업은 자바를, 스타트업은 Node.js</h2>
<p>대기업은 오랫동안 Java + Spring을 중심으로 서버를 구축하였기 때문에 이를 경험한 개발자 풀도 많은상태입니다. 스타트업은 속도와 유연성을 중요시하기 때문에 Node.js를 선호하는 경우가 많습니다. JavaScript를 그대로 백엔드로 확장할 수 있기때문입니다.</p>
<h2 id="2-es6와-자바스크립트">2. ES6+와 자바스크립트</h2>
<p>자바스크립트는 ECMAScript 사양을 바탕으로 표준화되어 발전해왔습니다. 특히 2015년에 발표된 <strong>ES6(ECMScripte 2015)</strong>는 자바스크립트 역사에서 중요한 전환점으로 평가됩니다.</p>
<p>let, const, 화살표함수, 템플릿 리터널, 모듈시스템 등 현대적 언어 기능이 본격적으로 도입되었습니다.</p>
<p>ES6이후에 등장한 기능들을 <strong>ES6+</strong>라고 불러집니다.</p>
<p>ES6이전 코드는 변수 스코프가 명확하지 않거나, 객체지향 구조를 표현하기 어려운 등코드 재사용성과 유지보수성이 떨어졌습니다.</p>
<h1 id="변수와-함수">변수와 함수</h1>
<p>변수와 함수는 자바스크립트에서 중요한 역할을 맡고 있습니다. 자바스크립트는 동적 타입 언어이고 함수가 일급객체로 취급되기 때문에 함수 활용도가 높습니다
변수와 함수는 <strong>UI 구성 전반에 관여하는 핵심 도구</strong>입니다.</p>
<h2 id="1-변수-선언-방식과-스코프의-이해">1. 변수 선언 방식과 스코프의 이해</h2>
<p>변수는 총 세가지로 var, let, const가 있습니다. ES6+이후로는 let, const를 주로 사용합니다.</p>
<p>var는 함수 스코스입니다. 즉 var는 함수 전체에서 접근이 가능합니다. 그래서 var로 선언한 코드들은 호이스팅(함수 맨위로 끌어올려지는 뜻)때문에 예기치 못한 함수명 중복이나 변수 값 변경때문에 오류가 발생할 수 있습니다.</p>
<p><strong>var</strong></p>
<pre><code>var x;
console.log(x)

x = 20</code></pre><p>해당코드는 호이스팅때문에 값이 undefined로 나타납니다. 이처럼 에러가 일어나기 쉽습니다.</p>
<p><strong>let, const</strong></p>
<pre><code>if(true) {
    let a = 10
}

console.log(a)</code></pre><p>해당 코드는 블록 스코프이기 때문에 ReferenceError가 뜬다.</p>
<p>이처럼 스코프의 명확함때문에 안정적인 코드를 작성할 수 있습니다.</p>
<ol>
<li><strong>lex = 값을 바꿀 수 있음</strong></li>
<li><strong>const = 값을 바꿀 수 없음</strong></li>
</ol>
<p>⚠ 여기서 주의할 점은 const는 변수 선언 그 자체를 저장하는것입니다. 즉 const의 값안에 값이 있을 경우 해당값은 변경할 수 있습니다.</p>
<pre><code>const a = [&#39;값&#39;, &#39; 변경가능한가요?&#39;]
a.push(&#39;네 가능합니다&#39;)
console.log(a.join(&#39;&#39;)) // 값 변경가능한가요? 네 가능합니다

const user = {name: &#39;영희&#39;}
user.name = &#39;철수&#39;</code></pre><h2 id="2-함수-선언-방식-비교">2. 함수 선언 방식 비교</h2>
<h3 id="1-함수-선언식">1. 함수 선언식</h3>
<pre><code>greet(&#39;연희&#39;)

function greet(name) {
    return `안녕? ${name}`
}</code></pre><p>가장 전통적인 선언 방식이며, 호이스팅이 일어나 함수 맨 위에 선언하더라도 에러가 나지 않습니다.</p>
<h3 id="2-함수-표현식">2. 함수 표현식</h3>
<pre><code>greet(&#39;연정&#39;) // ReferenceError
const greet = function(name) {
    return `안녕? ${name}`
}

// 동적 함수 구성
const numbers = [1, 2, 3];

// 배열의 map 메서드 안에서 이름 없는 함수(익명 함수)를 즉석에서 만들어 전달함
const doubled = numbers.map(function(num) {
  return num * 2;
});

console.log(doubled); 
// 결과: [2, 4, 6]</code></pre><p>호이스팅이 발생하지 않으며, 동적으로 함수를 구성하거나, 조건에 따라 함수를 선언해야할 때 유용합니다</p>
<h3 id="3-화살표-함수">3. 화살표 함수</h3>
<p>ES6에 도입된 함수입니다. 간결하게 표현할 수 있으며 this를 바인딩 하지 않습니다. 즉 this가 결정되는 기준은 선언될 때 정해집니다. (일반함수는 호출할 때 결정납니다)</p>
<p>이 때문에 클래스 내부나 콜백 함수에서 this를 다룰 때 유용합니다.</p>
<ol>
<li><p>일반 함수</p>
<pre><code>const user = {
 name: &#39;연정&#39;,
 hi: function() {
     console.log(`안녕 ${this.name}`) // 연정

     setTimeout(function() {
         console.log(`안녕 ${this.name}`) // undefined
     }, 1000)
 }
}</code></pre></li>
<li><p>화살표 함수</p>
<pre><code>const user = {
 name: &#39;연정&#39;,
 hi: function() {
     setTimeout(() =&gt; {
         console.log(`안녕 ${this.name}`) // 안녕 연정
     }, 1000)
 }
}</code></pre></li>
</ol>
<h3 id="4-즉시-실행-함수-표현식-iife">4. 즉시 실행 함수 표현식 (IIFE)</h3>
<p>함수를 정의하자마자 실행하는 표현식</p>
<pre><code>(function() {
    console.log(&#39;이 함수는 바로 실행됩니다&#39;)
})()</code></pre><h2 id="3-상황별-함수-선언-추천">3. 상황별 함수 선언 추천</h2>
<h3 id="1-함수-선언식-1">1. 함수 선언식</h3>
<p>호이스팅 되는 특성을 이용해 유틸함수에 사용하기 적합합니다. 무분별하게 사용하면 호출 시점이 예측하기 어려워 주의해야합니다.</p>
<ul>
<li>추천하는 상황: 전역적으로 사용되는 함수나 자주 호출되는 함수</li>
<li>주의할 점: 호이스팅과 함수 호출 시점에 대해 이해가 필요합니다.</li>
</ul>
<h3 id="2-함수-표현식-1">2. 함수 표현식</h3>
<p>함수를 변수에 선언하는 방식이므로 호이스팅되지 않습니다. 코드 흐름을 엄격하게 관리하고 싶은 프로젝트에 적합합니다.</p>
<ul>
<li>추천하는 상황: 함수의 선언과 호출을 엄격하게 관리할때 추천합니다.</li>
<li>주의할 점: 선언 이후에 호출할 수 있는점을 숙지해야합니다.</li>
</ul>
<h3 id="3-화살표-함수-1">3. 화살표 함수</h3>
<p>this가 고정된 스코프를 상속하기 때문에 콜백함수나 이벤트 핸들러에 자주 사용됩니다. 함수의 몸체가 짧고 간결하여 주로 사용됩니다.</p>
<ul>
<li>추천하는 상황: 콜백 함수, 이벤트 핸들러, 리액트의 useEffect, map 내부 렌더링 등</li>
<li>주의할 점: this가 상위 스코프에 고정되기 때문에, 객체의 메서드에서 동적 바인딩이 필요한 경우에는 적절하지 않습니다.</li>
</ul>
<h3 id="4-즉시-실행-함수-표현식">4. 즉시 실행 함수 표현식</h3>
<p>과거에는 전역 스코프를 보호하고 변수 충돌을 방지하기위해 자주 사용되었습니다.</p>
<ul>
<li>추천하는 상황: 외부에서 접근할 수 없는 변수나 함수를 만들어야할 때, 전역 스코프 오염을 방지할때</li>
<li>주의할 점: 현재는 import, export, class, let, const 등의 기능이 등장하면서 사용빈도가 줄어들었습니다.</li>
</ul>
<h2 id="4-함수는-일급-객체">4. 함수는 일급 객체</h2>
<p>자바스크립트에서 함수는 값처럼 다룰 수 있습니다. 함수가 변수에 할당할 수 있으며, 함수의 인자로 전달되거나 다른 함수의 반환값이 될 수 있습니다.</p>
<h3 id="1-함수를-변수에-할당하기">1. 함수를 변수에 할당하기</h3>
<pre><code>const hello = () {
    console.log(&#39;hi&#39;)
}</code></pre><p>함수를 변수(const)에 할당하여 사용할 수 있습니다.</p>
<h3 id="2-함수를-인자로-전달하기콜백함수">2. 함수를 인자로 전달하기(콜백함수)</h3>
<pre><code>function greet(callback) {
    console.log(&#39;hi&#39;)
    callback()
    console.log(&#39;bye&#39;)
}

greet(function () {
    console.log(&#39;lol&#39;)
})</code></pre><p>함수를 다른 함수의 인자로 전달, 내부에서 실행 가능합니다. 비동기 처리, 이벤트 처리, 애니메이션, useEffect, map렌더 등 매우 자주 등장합니다.</p>
<h3 id="3-함수를-반환값으로-사용하기고차함수">3. 함수를 반환값으로 사용하기(고차함수)</h3>
<pre><code>function multiple(factory) {
    return function(x) {
        return x * factory
    }
}

const double = multiply(2)
console.log(double(5)) // 10</code></pre><p>함수를 반환하는 함수, 즉 고차함수입니다. </p>
<h3 id="4-this와-화살표-함수의-차이">4. this와 화살표 함수의 차이</h3>
<p>일반 함수는 호출되는 방식에 따라 this가 달라지고, 화살표 함수는 this가 고정됩니다.</p>
<pre><code>const user = {
    name: &#39;alice&#39;,
    hi: function() { // 일반 함수
        console.log(this.name) // alice
    }
}

const user = {
    name: &#39;alice&#39;,
    hi: () =&gt; { // 화살표 함수
        console.log(this.name) // undefined
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[챕터1- 리액트란?]]></title>
            <link>https://velog.io/@songha-5/%EC%B1%95%ED%84%B01-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%9E%80</link>
            <guid>https://velog.io/@songha-5/%EC%B1%95%ED%84%B01-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%9E%80</guid>
            <pubDate>Fri, 29 May 2026 05:14:43 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-리액트의-인기">✨ 리액트의 인기</h1>
<hr>
<p>리액트는 현재 프론트엔드 개발에서 기본 전제로 여겨지는 표준 기술로 자리잡았습니다. 어떠한 요인때문에 리액트가 인기가 많아졌는지 알아보겠습니다.</p>
<h2 id="💻-리액트의-인기의-원인들">💻 리액트의 인기의 원인들</h2>
<hr>
<h3 id="리액트의-수요-증가">리액트의 수요 증가</h3>
<p>react, next.js를 다루는 개발자가 증가함으로 인해 기업이 안정적으로 인력을 확보하기 쉽습니다.
<img src="https://velog.velcdn.com/images/songha-5/post/54e7830e-6b31-42cd-896d-0c19422d0862/image.png" alt="">
<a href="https://2025.stateofjs.com/en-US/libraries/front-end-frameworks/">https://2025.stateofjs.com/en-US/libraries/front-end-frameworks/</a></p>
<h3 id="1-ai">1. AI</h3>
<p>개발을 도와주는 AI들이 만연합니다. 그 중 AI의 코드 가이드는 algular, vue보다 react가 보다 정확한, 보다 생산적인 코드를 만들어줍니다.</p>
<h3 id="2-기술-선택의-안정성">2. 기술 선택의 안정성</h3>
<p>api의 변화폭이 상대적으로 작고, 점진적 업데이트를 지향하고 있습니다. 그리고 오픈소스 라이브러리가 많으며, 리액트 개발자가 많다보니 수많은 레퍼런스를 가지고 있습니다.</p>
<h1 id="🕒-리액트의-철학과-등장">🕒 리액트의 철학과 등장</h1>
<hr>
<ol>
<li><strong>컴포넌트 단위 분리</strong> :  Card Component등 페이지에 구성 되어있는 화면에서 단위별로 나누어 쪼개어 개발합니다. 이렇게 되면 반복되는 UI들을 재사용할 수 있게되어 보다 효율적인 개발이 가능합니다.</li>
<li><strong>Virtgual DOM</strong> : Virtual DOM을 사용하여 달라진 부분만 리렌더링할 수 있게하여 보다 가볍게 관리할 수 있고 사용자경험도 증가시킬 수 있습니다.</li>
</ol>
<h2 id="리액트-발전과-변화">리액트 발전과 변화</h2>
<p>클래스 컴포넌트에서 함수 컴포넌트로 변경(Hook 도입)되며 상태관리와 라이프사이클제어를 선언적으로 처리할 수 있어, 코드가 간결하고 예측하기 좋아졌습니다.</p>
<h3 id="1-프론트엔드-개발-패러다임의-변화">1. 프론트엔드 개발 패러다임의 변화</h3>
<p>컴포넌트 단위로 분할하여 관리하는 방식이 확산됨에 따라, 개발자들은 복잡한 UI패턴 보다 효율적으로 설계하고 재사용할 수 있는 최적의 솔루션을 고민하게 되었습니다.</p>
<h3 id="2-서버-측-렌더링ssr과-정적-사이트-생성ssg">2. 서버 측 렌더링(SSR)과 정적 사이트 생성(SSG)</h3>
<p>프론트엔드 개발은 이제 클라이언트 렌더링중심에서 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG)으로 하이브리드 방식이 주류로 되고 있습니다. next.js와 같은 프레임워크를 활용해 보안, SEO등을 챙길수 있게되었습니다</p>
<h2 id="리액트-개발-환경">리액트 개발 환경</h2>
<p>리액트로 개발을 진행하려면 다양한 라이브러리와 개발환경 세팅이 필요합니다. babel, Webpack등이 활용되며, Typescript등이 추가되면 생산성과 안정성이 올라갑니다.</p>
<h3 id="1-리액트-버전-호환성">1. 리액트 버전 호환성</h3>
<p>리액트는 대규모 변화가 있을 때 한꺼번에 모든 것을 바꾸기보다, 점진적인 업데이트 방식을 채택하고 있습니다. 덕분에 개발자는 버전 업데이트 시 기존 코드를 대대적으로 수정해야 하는 부담을 줄이면서도, 최신 기능을 안정적으로 도입하여 일관된 사용자 경험을 유지할 수 있습니다.</p>
<h3 id="2-생태계의-핵심">2. 생태계의 핵심</h3>
<p>거의 모든 기능에 대해 신뢰할 수 있는 서드파티 라이브러리가 존재합니다. 예전에는 라이브러리의 코드호환성때문에 많은 시간을 소모해야했지만 리액트는 호환성 문제를 해결하였습니다.</p>
<h1 id="💥-리액트의-한계">💥 리액트의 한계</h1>
<hr>
<h2 id="리액트의-불편함">리액트의 불편함</h2>
<p>리액트는 라이브러리 이므로 상태관리나 라우팅 등 라이브러리들을 활용해야합니다. 그래서 프로젝트가 커질수록 의존성과 복잡성이 증가합니다.</p>
<p>Virtual DOM도 모든 상황에서 좋지 않습니다. 잦은 상태 변화나 대량 렌더링을 해야하는 상황에는 병목현상이 발생할 수 있습니다.</p>
<h3 id="1-virtual-dom이-정말-필요할까">1. Virtual DOM이 정말 필요할까?</h3>
<p>Virtual DOM은 바뀐 부분만 재렌더링해주는 좋은 기능입니다. 하지만 모든 상황에서 좋지는 않습니다. 상태변화가 잦고 수많은 요소가 동시에 갱신되는 대규모 애플리캐이션에는 오히려 병목현상을 일으킵니다.</p>
<p>그리고 요즘 웹 브라우저는 성능이 정말 좋아 해당 Virtual DOM의 오버헤드에 대해 의문이 제기되고 있습니다.</p>
<blockquote>
<p>컴파일 타임의 부상: Svelte나 Solid.js 같은 프레임워크는 가상 돔을 쓰는 대신, 빌드(컴파일) 시점에 어떤 코드가 돔을 변경할지 미리 파악하여 실제 돔을 직접 수정하는 방식을 택해 성능을 높이고 있습니다.</p>
</blockquote>
<h3 id="2-리액트의-갈라파고스화">2. 리액트의 갈라파고스화</h3>
<p>갈라파고스화는 특정 기술이 다른 흐름과 단절된 채 독자적인 방식으로 진화하는 현상을 이야기합니다. 리액트의 useState나 useEffect를 활용하여 웹을 만드는 경우, Vue나 바닐라JS에는 재사용이 불가능합니다</p>
<h2 id="복잡한-프론트엔드-생태계">복잡한 프론트엔드 생태계</h2>
<p>리액트의 특성상 연계되어있는 라이브러리들도 알아야 합니다. 그래서 학습 범위가 넓어지고, 라이브러리끼리 충돌이나 예외가 발생하는 경우가 있습니다.</p>
]]></description>
        </item>
    </channel>
</rss>