<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sangkun-svg.log</title>
        <link>https://velog.io/</link>
        <description>지식에 대한 두려움을 기록으로 극복하는 개발자</description>
        <lastBuildDate>Sun, 09 Feb 2025 12:19:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sangkun-svg.log</title>
            <url>https://velog.velcdn.com/images/sangkun-svg/profile/560c7112-abdf-4670-b7b3-6c755414a058/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sangkun-svg.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sangkun-svg" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TypeScript "declare"]]></title>
            <link>https://velog.io/@sangkun-svg/TypeScript-declare</link>
            <guid>https://velog.io/@sangkun-svg/TypeScript-declare</guid>
            <pubDate>Sun, 09 Feb 2025 12:19:08 GMT</pubDate>
            <description><![CDATA[<h1 id="declare란❓">declare란❓</h1>
<p>TypeScript에서 <strong><code>declare</code></strong>는 타입 선언을 할 때 사용됩니다.
이는 TypeScript에게 <strong><code>declare</code></strong>을 사용해 선언한 변수나 함수가 <strong>&quot;존재&quot;</strong>한다는것을 알려주는 역할을 하며, 실제 값의 구현은 TypeScript 외부에서 제공된다고 가정합니다.</p>
<h1 id="declare-언제-사용할까-🤔">declare 언제 사용할까? 🤔</h1>
<p><strong><code>declare</code></strong>는 주로 아래와 같은 경우에 사용합니다.</p>
<list>
    <ul>
      <li>
        라이브러리 사용 시
        <h5>
          라이브러리 사용 시 라이브러리의 타입을 TypeScript에서 알리기위해 <code>declare(declare module)</code>를 사용해 타입을 선언하여 사용자 환경에서 타입을 인식할 수 있도록 합니다.
        </h5>
        <p>
            아래 예시는 TypeScript에게 'demo-library'라는 라이브러리에 'demoFunction'이라는 함수가 있을 것이라고 알려줌으로 인해 다른 코드에서 사용할 수 있게됩니다. 다만 이 함수가 실제로 존재하는지는 확인할 수 없습니다.
        </p>
      </li>
    </ul>
</list>

<pre><code class="language-ts">    declare module `demo-library`  {
        export function demoFunction: void;
    }</code></pre>
<list>
    <ul>
      <li>
        전역 함수/변수 선언
        <h5>
          전역 변수나 함수는 실제로 코드 어디서든 참조될 수 있습니다. 이를                    <code>declare</code>로 선언하여 TypeScript에게 타입이 존재한다고 알려주는 방식입니다.
        </h5>
        <p>
          아래 예시는 TypeScript에게 <code>globalVariable</code>이 전역 변수로 선언하여 존재한다고 알리며, 해당 변수의 타입을 string으로 명시합니다. 이 변수는 코드 어디서든 참조될 수 있습니다.
        </p>
      </li>
    </ul>
</list>

<pre><code class="language-ts">    // 전역 변수 선언
    declare var globalVariable: string;

    // 전역 함수 선언
    declare function globalFunction(input:string): void;

    // 이후 코드에서 전역적으로 선언된 함수나 변수 사용 가능
    globalVariable = &quot;Hello globalVariable&quot;;
    globalFunction(globalVariable);</code></pre>
<list>
    <ul>
      <li>
        전역 타입 확장
        <h5>
          종종 코드에서는 전역 변수나 객체에 타입을 직접 추가해야 할 때가 있습니다.
          이럴 경우 TypeScript가 새로 추가된 프로퍼티를 인식하지 못하므로,                  <code>declare</code>키워드를 사용하여 해당 변수의 타입을 명시적으로 선언할 수 있습니다.
        </h5>
        <p>
          아래 예시는 TypeScript에게 <code>Window</code> 객체에 <code>NewFunction</code>이라는 함수를 추가한다고 알려줍니다. 이렇게 하면 window객체에서 NewFunction 함수를 사용할 수 있게 됩니다.
        </p>
      </li>
    </ul>
</list>

<pre><code class="language-ts">    declare global {
        interface Window {
            NewFunction: (): void;
        }
    }</code></pre>
<h1 id="마치며">마치며</h1>
<p>  <strong><code>declare</code></strong>는 타입 선언만을 제공하고, 실제 구현은 외부에서 제공된다고 TypeScript에게 알리는 역할을 합니다. 이를 통해 타입 안전성을 유지하면서 외부 라이브러리나 전역 변수, 전역 함수 등을 사용할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 'Never']]></title>
            <link>https://velog.io/@sangkun-svg/TypeScript-Never</link>
            <guid>https://velog.io/@sangkun-svg/TypeScript-Never</guid>
            <pubDate>Sun, 09 Feb 2025 12:18:27 GMT</pubDate>
            <description><![CDATA[<h2 id="never-타입이란">Never 타입이란?</h2>
<p>TypeScript에서 <strong><code>never</code></strong> 타입은 절대 발생하지 않는 값의 타입을 의미합니다.
즉, <strong><code>never</code></strong> 타입의 변수는 어떤 값(타입)도 가질 수 없으며, 함수는 어떤 값(타입)을 반환하지 않습니다.</p>
<h2 id="never-은-왜-필요할까-🤔">Never 은 왜 필요할까? 🤔</h2>
<h4 id="1-타입-안전성-🛡️">1. 타입 안전성 🛡️</h4>
<p> <strong><code>never</code></strong>  타입을 사용하면 코드에서 강한 타입 안정성을 보장할 수 있습니다.
switch 문에서 모든 경우를 다루지 않았을 경우, TypeScript는 컴파일 시점에 오류를 발생시켜 실수를 방지하는 데 도움을 줍니다.</p>
<pre><code class="language-ts">type Animal = &quot;dog&quot; | &quot;cat&quot;;

const getSound = (animal: Animal): string =&gt; {
  switch (animal) {
    case &quot;dog&quot;:
      return &quot;멍멍&quot;;
    case &quot;cat&quot;:
      return &quot;야옹&quot;;
    default:
      const neverValue: never = animal; // 오류 발생
      return neverValue;
  }
}</code></pre>
<h4 id="2-불가능한-코드-검출-🚫">2. 불가능한 코드 검출 🚫</h4>
<p><strong><code>never</code></strong> 타입을 활용하면, 존재할 수 없는 값(타입)이 실수로 코드에 사용되는 것을 방지할 수 있습니다.</p>
<pre><code class="language-ts">type Impossible = string &amp; number; // never</code></pre>
<p>예를 들어, 어떠한 값도 string과 number 타입을 동시에 가질 수 없기에 <code>Impossible</code>은 자동으로 <strong><code>never</code></strong> 타입이 됩니다.</p>
<h4 id="3-타입-체킹-강화-🔍">3. 타입 체킹 강화 🔍</h4>
<p>함수의 반환 타입이 <strong><code>never</code></strong> 타입이라면, 해당 함수는 절대 값을 반환하지 않음을 명확하게 나타낼 수 있습니다. 예를 들어, 예외를 던지는 함수가 이에 해당합니다.</p>
<pre><code class="language-ts">const throwError = (message: string): never =&gt; {
  throw new Error(message);
}</code></pre>
<p>위 함수는 무조건 예외를 발생시키기 때문에, 어떤 값도 반환하지 않으며, <strong><code>never</code></strong> 타입이 적절합니다.</p>
<h2 id="void와-never의-차이-⚖️">Void와 Never의 차이 ⚖️</h2>
<p><strong><code>never</code></strong>와 <strong><code>void</code></strong>는 비슷해 보이지만 의미와 사용 목적이 완전히 다릅니다.</p>
<h3 id="void"><code>Void</code></h3>
<ul>
<li><strong><code>void</code></strong> 타입은 <strong>아무것도 반환하지 않는 함수의 반환 타입</strong>입니다.</li>
<li>실제 반환값은 <strong><code>undefined</code></strong>입니다.</li>
<li>함수가 실행을 마친 후 <strong><code>undefined</code></strong> 반환하고 종료됩니다.</li>
</ul>
<pre><code class="language-ts">function sayHello(): void {
  console.log(&quot;Hello!&quot;);
}

const result = sayHello();
console.log(result); // undefined</code></pre>
<h3 id="never"><code>Never</code></h3>
<ul>
<li><strong><code>never</code></strong> 타입의 의미는 <strong>절대 반환되지 않는 함수의 반환 타입</strong>입니다.</li>
<li>실제 반환값이 없으며, 함수가 실행 중 예외를 던지거나 종료되지 않습니다.<pre><code class="language-ts">function throwError(message: string): never {
throw new Error(message);
}
</code></pre>
</li>
</ul>
<p>function infiniteLoop(): never {
  while (true) {
    console.log(&quot;무한 루프 실행 중...&quot;);
  }
}</p>
<pre><code>

## 결론 📝 
**`never`** 타입은 절대 실행되지 않거나 존재할 수 없는 값을 의미하는 타입입니다. 이를 통해 타입 체킹을 강화하고, 무한 루프나 오류를 발생시키는 함수에 적절한 타입을 적용할 수 있습니다.
**`void`** 타입과는 다르게 **`never`** 타입은 반환값이 아예 없으며, 사용 목적 또한 다릅니다.

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 'Any' vs 'Unknown']]></title>
            <link>https://velog.io/@sangkun-svg/TypeScript-Any-vs-Unknown</link>
            <guid>https://velog.io/@sangkun-svg/TypeScript-Any-vs-Unknown</guid>
            <pubDate>Sun, 09 Feb 2025 12:08:49 GMT</pubDate>
            <description><![CDATA[<h2 id="any와-unknown의-등장-배경-📜">Any와 Unknown의 등장 배경 📜</h2>
<h3 id="any-typescript-초기부터-존재한-유연한-타입-🔧">Any: TypeScript 초기부터 존재한 유연한 타입 🔧</h3>
<ul>
<li>Any타입은 <strong>TypeScript 1.0</strong>부터 존재한 타입입니다. Any는 모든 타입을 허용하는 유연한 성질을 가지고있었습니다. 예를 들어 외부 라이브러리에서 타입 정보를 제공하지 않는 경우 Any타입을 사용하는 경우가 많았습니다.</li>
</ul>
<h3 id="unknown-더-안전한-대체-타입의-등장-🔒">Unknown: 더 안전한 대체 타입의 등장 🔒</h3>
<ul>
<li><p>Unknown타입은 <strong><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type">TypeScript 3.0</a></strong>에 새로 출시된 타입입니다. unknown은 any와 유사하지만, 보다 타입 안전성이 보장되는 더 엄격한 타입입니다.
즉, 모든 타입은 unknown에 할당할 수 있지만, 반대로 unknown을 다른 타입에 할당하려면 반드시 타입 검사를 거쳐야 합니다.</p>
<pre><code class="language-ts">  let value: unknown;

  value = &quot;Hello&quot;;   // ✅ 모든 타입 할당 가능
  value = 42;        // ✅ 숫자 할당 가능
  value = true;      // ✅ 불리언 할당 가능

  let str: string = value; // ❌ 오류 발생 (타입 검사 필요)</code></pre>
</li>
</ul>
<h2 id="any와-unknown-무엇이-다를까-⚖️">Any와 Unknown, 무엇이 다를까? ⚖️</h2>
<p><img src="https://velog.velcdn.com/images/sangkun-svg/post/a97e0bcf-387e-4888-a6fc-46708087c554/image.png" alt="npm-trends-typescript&amp;react&amp;next"></p>
<p>이제 대부분의 프론트엔드 개발자들이 TypeScript를 사용하게 되었고, 그에 따라 라이브러리들도 대부분 TypeScript를 지원하는 상황입니다. 개인적으론 불가피한 경우를 제외하면 타입 안전성과 예측 가능한 코드를 작성하기 위해선 unknown 타입 사용을 지향하는게 좋다고 생각합니다.</p>
<h2 id="unknown-타입-어떻게-활용할까-💡">&quot;Unknown 타입, 어떻게 활용할까? 💡</h2>
<p>Unknown 타입의 변수를 다른 타입에 직접 할당하려고 하면 오류가 발생합니다.
이 문제를 해결하기 위해선 아래의 방식을 사용하여 할당하여야 합니다.</p>
<ol>
<li><p><strong>타입 단언(Type Assertion)</strong></p>
<pre><code class="language-ts">let unknownValue: unknown = &quot;TypeScript&quot;;
let stringValue: string = unknownValue as string;

console.log(stringValue.toUpperCase()); // &quot;TYPESCRIPT&quot;</code></pre>
<p>TypeScript에서는 시스템이 추론 및 분석한 타입 내용을 우리가 원하는 대로 얼마든지 바꿀 수 있습니다. 이때 &quot;<strong>타입 단언(type assertion)</strong>&quot;이라 불리는 메커니즘이 사용됩니다. TypeScript의 타입 단언은 개발자가 해당 값의 타입을 확신할 때는 <strong>&quot;as&quot;</strong> 키워드를 사용하여 타입을 단언할 수도 있습니다.
하지만 type assertion을 사용한다는 것은 개발자가 선언한 타입에 의존하기때문에 any를 사용할 때와 같이 타입 안정성을 보장하는 unknown의 장점을 무력화할 수 있습니다.</p>
<pre><code class="language-ts">let value: unknown = 42; // 숫자를 저장

let str: string = value as string; // 🚨 잘못된 단언이지만 컴파일러가 허용함

console.log(str.toUpperCase()); // ❌ 런타임 오류 발생</code></pre>
</li>
</ol>
<ol start="2">
<li><p>*<em>타입 좁히기(Type Narrowing): 안전한 방식으로 타입 확인하기 🛡️ *</em></p>
<pre><code class="language-ts"> function processValue(value: unknown) {
   if (typeof value === &quot;string&quot;) {
     console.log(value.toUpperCase()); // ✅ 안전하게 사용 가능
   }
 }

 processValue(&quot;hello&quot;); // &quot;HELLO&quot;
 processValue(42);      // 아무 일도 안 일어남
</code></pre>
<p> TypeScript에선 Narrowing은 특정 타입으로 제한하는 작업을 의미합니다. 이를 통해 코드 내에서 더 안전한 작업이 가능해지고, 오류를 줄일 수 있습니다. 
 따라서 unknown 타입을 사용할 땐 타입 선언보다 타입 좁히기를 추천드립니다.</p>
</li>
</ol>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>



<p>참고자료
<a href="https://www.typescriptlang.org/docs/">TypeScript Documentation</a>
<a href="https://radlohead.gitbook.io/typescript-deep-dive">typescript-deep-dive</a>
<a href="https://velog.io/@hwisaac/Typescript-narrowing">hwisaac: Typescript-narrowing</a>
<a href="https://f-lab.kr/insight/typescript-any-never-unknown-20240623">f-lab: typescript-any-never-unknown</a>
<a href="https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8-%EC%A2%85%EB%A5%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC#%ED%83%80%EC%9E%85">inpa dev: 타입스크립트 타입 선언 &amp; 종류 💯 총정리
</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모르면 안되는 React 안티패턴]]></title>
            <link>https://velog.io/@sangkun-svg/%EB%AA%A8%EB%A5%B4%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-React-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4-10%EA%B0%80%EC%A7%80</link>
            <guid>https://velog.io/@sangkun-svg/%EB%AA%A8%EB%A5%B4%EB%A9%B4-%EC%95%88%EB%90%98%EB%8A%94-React-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4-10%EA%B0%80%EC%A7%80</guid>
            <pubDate>Sat, 08 Feb 2025 11:44:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React 안티패턴에 대해 알아보기 위해 작성하였으며, 안티패턴에 대한 주관적인 해결법을 같이 공유합니다.</p>
</blockquote>
<h2 id="1-props-drilling">1. Props Drilling</h2>
<p><img src="https://media.geeksforgeeks.org/wp-content/uploads/20210618101141/Untitled.png" alt="geeksforgeeks:what-is-prop-drilling-and-how-to-avoid-it"></p>
<p><strong>Prop drilling</strong>은 부모 컴포넌트에서 여러 중간 컴포넌트를 거쳐 깊이 중첩된 자식 컴포넌트로 props를 전달하는 프로세스를 말합니다.
이때 해당 state가 최종적으로 자식 컴포넌트에 도달할 때까지 실제로 state가 불필요한 여러 자식컴포넌트에 해당 state를 전달하는것이 보통입니다.
이를 Props drilling이라고 합니다.</p>
<blockquote>
<p>Props drilling 패턴을 피하기 위해선 <strong>Context API, Redux, Zustand</strong> 와 같은 전역 상태 관리 라이브러리를 사용을 추천합니다.</p>
</blockquote>
<h2 id="2-props-plowing">2. Props Plowing</h2>
<pre><code class="language-jsx">const data = {
 id: 11,
 name: Yosua,
 age: 10,
 avatar: &quot;👍&quot;,
 bio: &quot;GGWP&quot;
}

// Props Plowing 상황
&lt;AssessmentTable id={data.id} name={data.name} age={data.age} avatar={data.avatar} bio={data.bio}/&gt;
// Spread operator를 사용해 간단하게 나다낸 모습
&lt;AssessmentTable {...data}/&gt;
// lodash.pick 함수를 사용해 명시적으로 데이터를 전달
&lt;AssessmentTable {..._.pick(data, [&quot;id&quot;, &quot;name&quot;, &quot;age&quot;, &quot;avatar&quot;, &quot;bio&quot;])} /&gt;</code></pre>
<p>Props Drilling은 수직적인 문제라면, <strong>Props Plowing</strong>은 수평적인 문제입니다.
이 패턴은 하나의 컴포넌트가 <strong>너무 많은 props를</strong> 가지게 되고, 결국 각 props가 전달되는 변수와 동일한 이름을 가지는 길고 반복적인 코드가 생성되는 상황을 말합니다.</p>
<blockquote>
<p>Props Plowing 패턴을 피하기 위해선 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax">JavaScript의 <strong>Spread Operator</strong></a>을 사용하여 props의 모든 key-value 쌍을 한번에 다 넘겨줄 수 있습니다. 다만 이 방법을 사용 시 불필요한 데이터까지 전달되거나 코드의 가독성이 떨어질 수 있습니다. 이럴땐 <a href="https://lodash.com/docs/4.17.15#pick"><strong>Lodash의 pick 함수</strong></a>를 통해 개선할 수 있습니다.</p>
</blockquote>
<h2 id="3-component-nesting컴포넌트-중첩">3. Component Nesting(컴포넌트 중첩)</h2>
<pre><code class="language-jsx">const Parent = () =&gt; {

  const SecondChild = () =&gt; {
    return &lt;div&gt; &lt;SecondChild/&gt; &lt;/div&gt;
  }

  const Child = () =&gt; {
    return &lt;div&gt; &lt;SecondChild/&gt; &lt;/div&gt;
  }

  return (
    &lt;div&gt;
     &lt;Child/&gt;
    &lt;/div&gt;
  )
</code></pre>
<p>중첩 컴포넌트는 자식 컴포넌트를 부모 컴포넌트 내부에 선언했을 때 문제가 생길 수 있는 상황입니다.
중첩 컴포넌트의 가장 큰 문제는 부모 컴포넌트가 렌더링될 때마다 자식 컴포넌트도 재정의되어 새로운 메모리 주소를 갖게 되고, 이로 인해 성능 문제와 예측할 수 없는 동작이 발생할 수 있다는 것입니다.</p>
<blockquote>
<p>Component Nesting 패턴을 피하기 위해선 <strong>컴포넌트를 별도의 파일로 분리하는 것</strong>이 가장 좋은 방법입니다.
만약 꼭 중첩 선언해야 한다면 <strong>useMemo</strong>나 <strong>useCallback을</strong> 활용해 <strong>메모이제이션</strong>할 수 있지만,
부모 컴포넌트의 props가 변경되면 여전히 자식이 재정의되므로 가능하면 분리하는 것이 최선입니다.</p>
</blockquote>
<h2 id="4-coupled-state-결합된-상태">4. Coupled State (결합된 상태)</h2>
<pre><code class="language-jsx">const UserProfile = () =&gt; {
  const [user, setUser] = useState({
    name: &quot;John Doe&quot;,
    age: 30,
    email: &quot;john@example.com&quot;
  });

  const updateName = () =&gt; {
    setUser(prev =&gt; {...prev, name: &quot;Jane Doe&quot;});
  };

  return (
    &lt;div&gt;
      &lt;p&gt;Name: {user.name}&lt;/p&gt;
      &lt;p&gt;Age: {user.age}&lt;/p&gt;
      &lt;p&gt;Email: {user.email}&lt;/p&gt;
      &lt;button onClick={updateName}&gt;Update Name&lt;/button&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>React에서 useState를 사용할 때, 여러 개의 상태 값을 하나의 객체에 넣고 관리하고 싶을 때가 있습니다.
이렇게 하면 코드가 더 깔끔해 보이고, setState를 한 번만 호출할 수 있기 때문에 성능도 좋아 보일 수 있습니다.</p>
<p>하지만 모든 상태를 하나의 객체로 묶는 방식은 유지보수와 가독성을 해칠 수 있습니다. 또한 지금은 성능에 영향이 없지만 React 18 이전에는 setState를 여러 번 호출하면 리렌더링이 여러 번 발생하는 문제가 있었습니다.</p>
<blockquote>
<p>Coupled state 패턴을 피하려면, 상태를 독립적으로 선언하여 개별적으로 관리하는 것이 좋습니다. 이를 통해 Single Source of Truth(신뢰할 수 있는 단일 출처) 원칙을 유지하고, 데이터 무결성을 보장하며, 불필요한 사이드 이펙트를 방지할 수 있습니다.</p>
</blockquote>
<h2 id="5-using-index-as-keyindex를-key로-사용">5. Using Index as key(index를 key로 사용)</h2>
<pre><code class="language-jsx">{items.map((item, index) =&gt; (
  &lt;Item key={index} data={item} /&gt;
))}

Instead, use a unique identifier for each item as the key.</code></pre>
<p>배열 형식의 데이터를 렌더링할 때 key 값으로 배열의 index를 사용하는 경우가 종종 있습니다. 이는 데이터가 변경될 경우 React의 가상 DOM이 기존 요소와 새로운 요소를 올바르게 매칭하지 못하면서 불필요한 렌더링이 발생하는 문제를 일으킬 수 있습니다.</p>
<blockquote>
<p>위의 패턴을 피하기 위해선 리스트의 각 항목에 unique한 데이터를 사용하여 key 값으로 지정하는것이 좋습니다. 데이터에 unique한 데이터가 없다면 <a href="https://www.npmjs.com/package/uuid">uuid 같은 라이브러리</a>를 활용해 고유 식별자를 생성하는 것을 권장합니다.</p>
</blockquote>
<h2 id="6-direct-manipulation-of-the-dom">6. Direct Manipulation of the DOM</h2>
<p>리액트는 기본적으로 가상 DOM을 사용하여 UI를 업데이트합니다. 그러나 때때로 리액트 외부에서 직접 DOM을 조작하는 경우가 있습니다. document.getElementById()와 같은 방법을 사용하여 DOM을 직접 수정하면 리액트의 상태 관리 시스템과 충돌할 수 있습니다.</p>
<blockquote>
<p>위 패턴을 피하기 위해선 DOM 조작을 리액트의 상태 관리와 결합하여 사용하고, 가능하면 ref를 활용하여 DOM에 접근하는 것을 권장합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴포넌트안에 컴포넌트안에 컴포넌트안에 컴포넌트안에 ]]></title>
            <link>https://velog.io/@sangkun-svg/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90</link>
            <guid>https://velog.io/@sangkun-svg/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%95%88%EC%97%90</guid>
            <pubDate>Wed, 22 Jan 2025 11:49:05 GMT</pubDate>
            <description><![CDATA[<h3 id="들어가며">들어가며</h3>
<p>항상 프로젝트를 시작 할 때하다 더 좋은 코드, 더 좋은 구조 등의 코드를 작성하리라 다짐하지만, 시간이 지날수록 임박하는 데드라인, 자주 바뀌는 기획으로 인해 복잡성이 증가하고 가독성이 떨어지는 순간이 많아졌습니다. 
이러한 문제를 해결하기 위해서 좋은 코드를 작성하려는 노력보단 나쁜 코드를 피하기 위해 안티패턴에 대해 공부한 내용을 작성하려 합니다.</p>
<h3 id="컴포넌트-안에-컴포넌트를-만드는-것은-나쁜-습관인가요">컴포넌트 안에 컴포넌트를 만드는 것은 나쁜 습관인가요?</h3>
<p>React에서는 중첩된 컴포넌트를 만들 수 있지 않나요?
다른 컴포넌트 안에 컴포넌트를 만드는 것은 편리해 보일 수 있지만 몇 가지 문제가 발생할 수 있습니다.</p>
<pre><code class="language-jsx">export default function ParentComponent(props) {
  function InnerComponent() {
    // ...
  }

  return (
    &lt;&gt;
      &lt;InnerComponent /&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>우선, 중첩된 컴포넌트는 부모 컴포넌트와 <strong>긴밀하게 결합되어 재사용성이 떨어집니다</strong>. 
<strong>내부 컴포넌트는 내보내서 재사용할 수 없고</strong>, 대신 다른 컴포넌트에 소품으로 전달되어 모듈식 코드가 줄어듭니다.</p>
<p>또한 컴포넌트 내에서 컴포넌트를 만들면 <strong>성능 문제가 발생할 수 있습니다</strong>. 내부 컴포넌트의 선언 함수는 상위 컴포넌트의 렌더링 주기마다 다시 생성됩니다. 이로 인해 프로젝트의 규모가 커질수록 성능에 영향이 갈 수 있습니다.</p>
<p>React에서는 중첩 컴포넌트 생성을 명시적으로 금지하지는 않지만 일반적으로 다른 컴포넌트 내에서 컴포넌트를 생성하지 않는 것이 좋습니다. 
반면에 컴포넌트를 독립적으로 선언하여 관리하면 모듈성과 재사용성을 보장할 수 있습니다.</p>
<p>다른 파일에 별도의 컴포넌트를 만든 다음 다른 컴포넌트에서 import 하는 방식으로 이를 방지할 수 있습니다.</p>
<pre><code class="language-jsx">const InnerComponent = (props) =&gt; {
    // ...
}

export default InnerComponent;</code></pre>
<pre><code class="language-jsx">import InnerComponent from &#39;./InnerComponent&#39;;

const ParentComponent = (props) =&gt; {
  return (
    &lt;&gt;
      &lt;InnerComponent /&gt;
    &lt;/&gt;
  );
}

export default ParentComponent;</code></pre>
<h3 id="그렇다면-중첩-컴포넌트는-절대-사용하면-안되나요">그렇다면 중첩 컴포넌트는 절대 사용하면 안되나요?</h3>
<p>그런것만은 아닙니다. 위에서 예시를 든 경우는 부모-자식 컴포넌트 각각 어느정도의 기능을 가지고 있다는 가정하에 작성한 것입니다. 
부모 컴포넌트 내에서만 작동하거나, 일회성으로만 작성되는 컴포넌트들은 부모 컴포넌트 내에 작성해도 무관하다고 생각합니다.
아래와 같이 간단한 컴포넌트는 작성해도 무관하다 생각합니다.</p>
<pre><code class="language-jsx">const ParentComponent = ({ data }) =&gt; {
  const InnerComponent = () =&gt; {
    return &lt;div&gt;Chart with data: {data.count}&lt;/div&gt;;
  }

  return (
    &lt;div&gt;
      &lt;h2&gt;Dashboard&lt;/h2&gt;
      &lt;InnerComponent /&gt;
    &lt;/div&gt;
  );
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[간단한 조건문을 작성하는 방식]]></title>
            <link>https://velog.io/@sangkun-svg/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EC%9D%84-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@sangkun-svg/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EC%9D%84-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Sun, 19 Jan 2025 09:56:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 Toss에서 운영하는 <a href="https://frontend-fundamentals.com/">Frontend-fundamentals</a> 의 커뮤니티에서 나온 주제를 바탕으로 작성되었습니다.
<a href="https://github.com/toss/frontend-fundamentals/discussions/41#discussioncomment-11875243">커뮤니티 바로 가기</a></p>
</blockquote>
<p>토론에서 간단한 If문을 작성하는 방식은 총 3가지로 소개됩니다.</p>
<pre><code class="language-js">export default function Foo() {
  if(A) return null; // 한 줄로 작성

  if(B) // 중괄호 없는 두 줄
    return null; 

  if(C) { // 중괄호 있는 세 줄
    return null;
  }
}</code></pre>
<p>저는 간단한 코드라면 짧은 코드가 더 가독성이 좋다는 생각을 가지고 있어 <strong>A 방식(한 줄)</strong>을 선호해왔습니다.
그리고 대부분의 개발자분들도 그렇지 않을까? 라는 추측을 하고 투표 결과를 보니 제 예상과는 다른 결과가 있었습니다. 
<img src="https://velog.velcdn.com/images/sangkun-svg/post/7b949527-93ae-4714-bd58-8508f8e0a975/image.png" alt=""></p>
<p>커뮤니티에서 진행된 투표 결과를 보면, <strong>C 방식(중괄호 있는 세 줄)</strong>이 가장 선호되는 것으로 나타났습니다.</p>
<h3 id="왜-c-방식이-더-많은-개발자들에게-선택받았을까요">왜 C 방식이 더 많은 개발자들에게 선택받았을까요?</h3>
<p>커뮤니티 토론을 바탕으로 요약하면, C 방식의 장점은 아래와 같습니다.</p>
<ul>
<li>스타일의 일관성: 간단한 조건문이라도 중괄호를 포함해 작성하면 팀 전체의 코드 스타일이 통일됩니다.</li>
<li>확장성: 조건문 내부에 추가 로직을 넣는 경우, 중괄호가 있으면 코드 수정이 더 쉽습니다.</li>
<li>형상 관리: Git Diff에서 변경점을 확인할 때, 중괄호가 있는 코드가 더 명확합니다.
이처럼 단순한 If문 작성 방식이라도 팀 협업과 코드 관리 측면에서 큰 차이를 만들 수 있습니다. </li>
</ul>
<p>개인적으로 <a href="https://github.com/toss/frontend-fundamentals/discussions/41?sort=new#discussioncomment-11875243">형상관리</a>까지 고려하시는 걸 보고 감탄이 나왔습니다...
형상관리 관련 댓글은 꼭 읽어보시는 걸 추천드립니다! 
저 또한 논의를 통해 기존에 선호하던 A 방식 대신 C 방식을 채택 할 것 같습니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 안티패턴: Setter를 하위 컴포넌트로 Prop으로 전달하는 패턴 지양하기 (번역)]]></title>
            <link>https://velog.io/@sangkun-svg/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4-Setter%EB%A5%BC-%ED%95%98%EC%9C%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-Prop%EC%9C%BC%EB%A1%9C-%EC%A0%84%EB%8B%AC%ED%95%98%EB%8A%94-%ED%8C%A8%ED%84%B4-%EC%A7%80%EC%96%91%ED%95%98%EA%B8%B0-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@sangkun-svg/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%95%88%ED%8B%B0%ED%8C%A8%ED%84%B4-Setter%EB%A5%BC-%ED%95%98%EC%9C%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-Prop%EC%9C%BC%EB%A1%9C-%EC%A0%84%EB%8B%AC%ED%95%98%EB%8A%94-%ED%8C%A8%ED%84%B4-%EC%A7%80%EC%96%91%ED%95%98%EA%B8%B0-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Fri, 17 Jan 2025 09:15:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 원저자의 허락을 맡아 &lt;<a href="https://matanbobi.dev/posts/stop-passing-setter-functions-to-components#comments">React Anti-Pattern: Stop Passing Setters Down the Components Tree</a>&gt;을 한국어로 번역한 글입니다.</p>
</blockquote>
<h2 id="intro">Intro</h2>
<p>최근에 저는 React 프로젝트에서 위험 신호를 보내는 몇 가지 패턴을 발견했습니다. 그 패턴 중 하나인 useState Setter를 프로퍼티로 전달하는 것을 살펴보겠습니다.</p>
<p>다음은 전형적인 예시입니다:</p>
<blockquote>
<p>다음 예제는 순전히 설명 목적으로 양식 컴포넌트를 사용하며, 개념은 보편적으로 적용되며 양식 처리의 세부 사항과 관련이 없습니다.</p>
</blockquote>
<pre><code class="language-jsx">// Form.jsx 
function Form() { 
    const [formData, setFormData] = useState({ name: &#39;&#39; }); 
    return (
        &lt;div&gt;
            &lt;h1&gt;Form&lt;/h1&gt; 
            {/* setter 함수를 자식 컴포넌트로 전달 */}       
            &lt;Input name={formData.name} setFormData={setFormData} /&gt;
            &lt;button onClick={() =&gt; console.log(formData)}&gt;Submit&lt;/button&gt; 
        &lt;/div&gt; 
    ); 
};

// Input.jsx
function Input({ name, setFormData }) { 
    const handleInputChange = (event) =&gt; { 
      // 부모 컴포넌트에게 받은 setter를 사용
        setFormData((prevData) =&gt; ({ ...prevData, name: event.target.value })); 
    }; 

    return ( 
        &lt;div&gt;
            &lt;label&gt; 
                Name: 
                &lt;input type=&quot;text&quot; value={name} onChange={handleInputChange} /&gt; 
            &lt;/label&gt;
        &lt;/div&gt; 
    ); 
};</code></pre>
<p>지금은 이 코드가 잘 작동하지만, 프로젝트가 발전하면서 왜 이런 방식이 문제를 일으킬 수 있는지 자세히 살펴보겠습니다.</p>
<h2 id="the-evolution-of-code">The evolution of code</h2>
<p>이제 Form을 개선해야 한다고 가정해 봅시다.
더 많은 필드가 추가되고 오류 처리가 도입되며 로직이 더 복잡해집니다. 
이를 관리하기 위해 <em><strong>useState</strong></em> 에서 <em><strong>useReducer</strong></em> 로 전환하기로 결정합니다.
리팩터링된 Form 컴포넌트의 모습은 다음과 같습니다.</p>
<pre><code class="language-jsx">function reducer(state, action) { 
    switch(action.type) {
        case &#39;setField&#39;: 
            return { 
                ...state, 
                [action.payload.fieldName]: action.payload.fieldValue 
            };
        case &#39;setError&#39;:
            return {
                ...state,
                error: action.payload.error
            }
        default:
            return state;
    }
}

function Form() {  
    const [formData, dispatch] = useReducer(reducer, { name: &#39;&#39;, error: null });
    return (
        &lt;div&gt;
            &lt;h1&gt;Form&lt;/h1&gt; 
            {/* What happens to setFormData now? */}
            &lt;Input name={formData.name} setFormData={setFormData} /&gt;
            &lt;button onClick={() =&gt; console.log(formData)}&gt;Submit&lt;/button&gt; 
        &lt;/div&gt; 
    ); 
};</code></pre>
<p>상태 관리 메커니즘을 변경했지만 이제 useState를 중심으로 설계된 <strong>Input</strong> 컴포넌트의 로직이 더 이상 작동하지 않습니다.
이제 Input 컴포넌트에 dispatch 함수를 전달하여 특정 액션을 디스패치하도록 해야 할까요? 바로 이 지점에서 추상화 누수가 문제가 됩니다.</p>
<h2 id="추상화-누수">추상화 누수</h2>
<p>추상화 누수는 한 컴포넌트가 다른 컴포넌트의 내부 구현에 대해 너무 많이 알고 있을 때 발생합니다.</p>
<p>이 경우 Input 컴포넌트는 다음과 같이 가정합니다</p>
<ol>
<li>부모 컴포넌트가 useState를 사용하고 있습니다.</li>
<li>상태에는 다른 데이터와 함께 이름 필드가 직접 포함되어 있습니다.</li>
<li>부모 컴포넌트는 항상 동일한 상태 구조를 유지합니다.</li>
</ol>
<p>이러한 가정으로 인해 자식 컴포넌트는 부모와 타이트하게 결합되므로 부모의 상태 구조나 관리 메커니즘이 변경되면 자식도 업데이트해야 합니다.</p>
<h2 id="이것이-왜-나쁜건가요">이것이 왜 나쁜건가요?</h2>
<ul>
<li>취약성: 상위 컴포넌트의 로직을 변경하면 하위 컴포넌트가 손상되어 유지 관리에 골칫거리가 됩니다.</li>
<li>재사용성 감소: 자식은 특정 부모 구현에 묶여 있어 다른 컨텍스트에서의 사용이 제한됩니다.</li>
<li>명확성 손실: 원시 setState를 전달하면 자식 컴포넌트가 무엇을 수정해야 하는지 불분명해집니다.</li>
</ul>
<h2 id="누수를-어떻게-해결할-수-있을까요">누수를 어떻게 해결할 수 있을까요?</h2>
<p>이 경우 추상화 누수를 해결하는 방법은 매우 간단합니다.
<em><strong>Input</strong></em> 컴포넌트는 실제 Setter 함수가 있는 프로퍼티를 받을 필요가 없습니다.
상태 변경을 캡슐화하는 콜백 함수를 가져올 수 있습니다. 
예를 들어, <em><strong>handleNameChange</strong></em> 라는 함수는 다음과 같습니다.</p>
<pre><code class="language-jsx">// Form.jsx 
function Form() { 
    const [formData, setFormData] = useState({ name: &#39;&#39; }); 
    const handleNameChange = (name) =&gt; { 
        setFormData((prevState) =&gt; ({...prevState, name}));
    };

    return (
        &lt;div&gt;
            &lt;h1&gt;Form&lt;/h1&gt; 
            {/* Pass the setter function down to Input */}
            &lt;Input name={formData.name} onChange={handleNameChange} /&gt;
            &lt;button onClick={() =&gt; console.log(formData)}&gt;Submit&lt;/button&gt; 
        &lt;/div&gt; 
    ); 
};

// Input.jsx 
function Input({ name, onChange }) { 
    const handleInputChange = (event) =&gt; { 
        onChange(event.target.value);
    }; 

    return ( 
        &lt;div&gt;
            &lt;label&gt; 
                Name: 
                &lt;input type=&quot;text&quot; value={name} onChange={handleInputChange} /&gt; 
            &lt;/label&gt;
        &lt;/div&gt; 
    ); 
};</code></pre>
<h2 id="요약">요약</h2>
<p>이 글에서는 <em><strong>useState</strong></em> 의 <em><strong>Setter</strong></em> 함수를 Props로 전달하는 것이 좋은 방법이 아닌 이유에 대해 설명했습니다. 
그렇게 하면 자식 컴포넌트가 부모의 구현에 긴밀하게 결합되어 추상화 누수가 발생합니다.
대신 캡슐화된 콜백 함수를 사용하면 <strong>명확성, 재사용성, 유지보수성</strong>이 어떻게 향상되는지 보여드렸습니다.</p>
]]></description>
        </item>
    </channel>
</rss>