<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>건망증 환자의 뇌장하드</title>
    <link>https://kmj24.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 20 Jul 2026 10:38:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kmj24</managingEditor>
    <image>
      <title>건망증 환자의 뇌장하드</title>
      <url>https://tistory1.daumcdn.net/tistory/4458576/attach/d24896bce4884003886b5850158b82ea</url>
      <link>https://kmj24.tistory.com</link>
    </image>
    <item>
      <title>Svelte5</title>
      <link>https://kmj24.tistory.com/243</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Rune 문법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Svelte5의 반응성 관리를 위한 키워드&lt;/li&gt;
&lt;li&gt;기존 문법에 비해 더 명시적인 문법을 제공&lt;/li&gt;
&lt;li&gt;rune 문법
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$state&lt;/li&gt;
&lt;li&gt;$derived&lt;/li&gt;
&lt;li&gt;$effect&lt;/li&gt;
&lt;li&gt;$props&lt;/li&gt;
&lt;li&gt;$bindable&lt;/li&gt;
&lt;li&gt;$inspect&lt;/li&gt;
&lt;li&gt;$host&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$state&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트에서 사용되는 상태를 의미함&lt;/li&gt;
&lt;li&gt;let 키워드로 변수처럼 선언되는 상태를 $state로 선언&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$derived&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;state의 값에서 파생한 다른 값을 만들 수 있음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;$derived.by&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;좀더 복잡한 값을 사용할 경우 함수로 넘겨줄 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$effect&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 변수 변경에 따른 부수 효과를 정의&lt;/li&gt;
&lt;li&gt;V4&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744529565091&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script lang=&quot;ts&quot;&amp;gt;
  let count = 0;

  $: if (count &amp;gt; 10 || count &amp;lt; -10) {
    count = 0;
  }
&amp;lt;/script&amp;gt;

&amp;lt;div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button onclick={() =&amp;gt; count++}&amp;gt;increase&amp;lt;/button&amp;gt;
    &amp;lt;button onclick={() =&amp;gt; count--}&amp;gt;decrease&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;span&amp;gt;count : {count}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;V5&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744529626359&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script lang=&quot;ts&quot;&amp;gt;
  let count = $state(0);

  $effect(() =&amp;gt; {
    if (count &amp;gt; 10 || count &amp;lt; -10) {
      count = 0;
    }
  });
&amp;lt;/script&amp;gt;

&amp;lt;div&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;button onclick={() =&amp;gt; count++}&amp;gt;increase&amp;lt;/button&amp;gt;
    &amp;lt;button onclick={() =&amp;gt; count--}&amp;gt;decrease&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;span&amp;gt;count : {count}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;svelte 컴파일러는 effect 내에서 실행되는 모든 코드의 state를 추적 및 감지하여 업데이트 시 함수를 실행한다.\&lt;/li&gt;
&lt;li&gt;&lt;b&gt;$effect.pre&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DOM 업데이트 이전에 실행된다.&lt;/li&gt;
&lt;li&gt;작동 원리는 $effect와 동일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;$effect.tracking : boolean&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드가 $effect에 의해 실행되는지 아닌지 여부를 알려줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주의사항
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$effect는 일반 변수의 변화를 감지하지 못한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;상태(&lt;b&gt;$state 또는 $derived&lt;/b&gt;)가 아닌 변수를 이용하여 조건분기할 경우 Svelte컴파일러는 변수의 변화를 감지하지 못하여 해당 조건 분기 내의 코드는 어떠한 경우에도 실행되지 않는다.&lt;/li&gt;
&lt;li&gt;조건분기가 필요할 경우 $state를 이용하여 선언하도록 하여 $effect가 감지할 수 있도록 해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744530096626&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let isOpen = false;

$effect(() =&amp;gt; {
	if (isOpen) {
    	// isOpen이 true로 바뀌더라도 isOpen이 state가 아닌 변수이므로 
        // $effect는 변수의 변화를 감지하지 못하여 이 블록 내 코드는 어떠한 경우에도 실행되지 않는다.
    }
});

&amp;lt;button onclick={() =&amp;gt; isOpen = !isOpen}&amp;gt;
	click
&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$Props&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하위 컴포넌트에 props 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;$bindable&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양방향 바인딩 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744529694143&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let { value = $bindable(), ...props } = $props();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;$inspect&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저 개발자모드에서 상태 변화 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Snippet&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 HTML 블럭을 표현할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744529778323&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Parent.svelte
// ...
{#snippet name()}
  &amp;lt;div&amp;gt;name&amp;lt;/div&amp;gt;
{/snippet}
&amp;lt;Children name={name} /&amp;gt;

// Children.svelte
// ...
{#if name}
    &amp;lt;div&amp;gt;
        {@render name()}
    &amp;lt;/div&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@render snippet() 으로 함수 실행하는것처럼 실행&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/243</guid>
      <comments>https://kmj24.tistory.com/243#entry243comment</comments>
      <pubDate>Sun, 13 Apr 2025 16:42:41 +0900</pubDate>
    </item>
    <item>
      <title>react-native로 간단한 Todo 앱 구현하기</title>
      <link>https://kmj24.tistory.com/242</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;react-native를 설치한데 이어 간단한 Todo앱을 구현해보았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input에 할일을 적고 add 버튼을 누르면 하단 리스트에 추가된다.&lt;/li&gt;
&lt;li&gt;checkbox를 클릭하면 todo에서 할일을 했다는 표시로 글자 가운데 줄이 그어진다.&lt;/li&gt;
&lt;li&gt;각 row는 delete 버튼이 있어서 리스트에서 제거할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;레이아웃&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native는 기본적으로 화면을 구성하기 위해 내장 컴포넌트인 View 컴포넌트를 사용하며, StyleSheet를 이용하여 스타일을 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StyleSheet는 css의 스타일 기능을 제공하며, camel case 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Container&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 화면을 구성하는 container&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native는 ios, android 모두 지원하므로 둘 다의 레이아웃을 적용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ios는 상단 노치에 의해 레이아웃이 가려지는 부분 때문에 추가적으로 설정을 해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SafeAreaView를 이용하여 수동으로 레이아웃을 잡아주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactnative.dev/docs/safeareaview&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://reactnative.dev/docs/safeareaview&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1717848589010&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SafeAreaView &amp;middot; React Native&quot; data-og-description=&quot;The purpose of SafeAreaView is to render content within the safe area boundaries of a device. It is currently only applicable to iOS devices with iOS version 11 or later.&quot; data-og-host=&quot;reactnative.dev&quot; data-og-source-url=&quot;https://reactnative.dev/docs/safeareaview&quot; data-og-url=&quot;https://reactnative.dev/docs/safeareaview&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dkrT6I/hyWg9Uc2BG/6kCjK9miQQRDFYqKLgJa40/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ykadW/hyWldACNim/qAXG7OTUkXOGdcL8xTPFk0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://reactnative.dev/docs/safeareaview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://reactnative.dev/docs/safeareaview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dkrT6I/hyWg9Uc2BG/6kCjK9miQQRDFYqKLgJa40/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/ykadW/hyWldACNim/qAXG7OTUkXOGdcL8xTPFk0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SafeAreaView &amp;middot; React Native&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The purpose of SafeAreaView is to render content within the safe area boundaries of a device. It is currently only applicable to iOS devices with iOS version 11 or later.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;reactnative.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1717850827453&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from 'react';
import {
  SafeAreaView,
  View,
  StyleSheet,
  Platform,
  type ViewStyle,
} from 'react-native';
import Contents from 'expo-constants';

const styles = StyleSheet.create({
  container: {
    paddingHorizontal: 16,
  },
});

type Props = {
  children: React.ReactNode;
  style?: ViewStyle;
};

function ViewTemplate({ children, style }: Props) {
  const padding = Platform.OS === 'ios' ? 16 : Contents.statusBarHeight + 16;

  return (
    &amp;lt;SafeAreaView&amp;gt;
      &amp;lt;View
        style={{
          ...styles.container,
          paddingTop: padding,
          ...style,
        }}
      &amp;gt;
        {children}
      &amp;lt;/View&amp;gt;
    &amp;lt;/SafeAreaView&amp;gt;
  );
}

export default ViewTemplate;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native에서 Platform을 통해 ios인지, android인지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를통해 OS별 분기처리가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 레이아웃은 위 컴포넌트로 구성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Input&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native의 TextInput컴포넌트를 이용하여 input을 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법은 html의 input태그와 사용하는것이 크지는 않지만, onChange이벤트는 onChangeText라는 props로 넘겨준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;Button&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-native에서 제공하는 Button컴포넌트가 있지만 스타일 적용이 까다로운것 같아서 다른 컴포넌트를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TouchableOpacity컴포넌트를 이용하여 스타일링이 용이한 컴포넌트를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 클릭이벤트는 onPress를 이용하여 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Checkbox&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에 react-native에서 Checkbox가 있었지만 현재는 사라진것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 서드파티 모듈에서 Checkbox를 찾아야 헀는데 expo-checkbox라는 라이브러리를 통해 Checkbox를 구현할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717851093824&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useRef, useState } from 'react';
import Typo from '@/components/Typo';
import ViewTemplate from '@/components/ViewTemplate';
import {
  StyleSheet,
  View,
  TextInput,
  Text,
  TouchableOpacity,
  Dimensions,
} from 'react-native';
import Checkbox from 'expo-checkbox';

const styles = StyleSheet.create({
  container: {
    paddingLeft: 12,
    paddingRight: 12,
  },
  title: {
    fontSize: 36,
    fontWeight: 'bold',
  },
  todoContainer: {
    rowGap: 16,
    width: '100%',
  },
  addArea: {
    flexDirection: 'row',
    columnGap: 16,
    height: 40,
  },
  input: {
    width: Dimensions.get('window').width - 140,
    borderRadius: 4,
    borderWidth: 1,
    height: 50,
    fontSize: 20,
    paddingHorizontal: 4,
  },
  addBtnBackground: {
    backgroundColor: '#d0d0d0',
    paddingVertical: 15,
    paddingHorizontal: 30,
    borderRadius: 4,
    marginBottom: 20,
    height: 50,
    justifyContent: 'center',
    alignItems: 'center',
  },
  addBtnText: {
    color: '#000',
    fontSize: 12,
  },
  todoListArea: {
    rowGap: 8,
  },
  row: {
    flexDirection: 'row',
  },
  left: {
    flexDirection: 'row',
    alignItems: 'center',
    columnGap: 4,
  },
  todoText: {
    width: Dimensions.get('window').width - 148,
  },
  deleteBtnBg: {
    backgroundColor: '#d0d0d0',
    justifyContent: 'center',
    alignItems: 'center',
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 4,
  },
  deleteBtnText: {
    color: '#000',
    fontSize: 12,
  },
});

export default function HomeScreen() {
  const inputRef = useRef&amp;lt;TextInput | null&amp;gt;(null);

  const [value, setValue] = useState('');
  const [list, setList] = useState&amp;lt;
    { key: number; value: string; checked: boolean }[]
  &amp;gt;([]);

  const onChangeText = (v: string) =&amp;gt; {
    setValue(v);
  };

  const onAdd = () =&amp;gt; {
    if (value === '') return;

    setList([
      ...list,
      {
        key: new Date().getTime(),
        value,
        checked: false,
      },
    ]);

    setValue('');

    inputRef.current?.blur();
  };

  const onDelete = (idx: number) =&amp;gt; {
    setList((prev) =&amp;gt; prev.filter((_, i) =&amp;gt; idx !== i));
  };

  const onChangeCheck = (idx: number, checked: boolean) =&amp;gt; {
    setList((prev) =&amp;gt;
      prev.map((state, i) =&amp;gt; ({
        ...state,
        checked: i === idx ? checked : state.checked,
      })),
    );
  };

  return (
    &amp;lt;ViewTemplate
      style={{
        padding: 0,
        rowGap: 14,
        backgroundColor: '#fff',
        height: '100%',
      }}
    &amp;gt;
      &amp;lt;Typo typo='t'&amp;gt;Todo List&amp;lt;/Typo&amp;gt;
      &amp;lt;View style={styles.todoContainer}&amp;gt;
        &amp;lt;View style={styles.addArea}&amp;gt;
          &amp;lt;TextInput
            style={styles.input}
            onChangeText={onChangeText}
            value={value}
            ref={inputRef}
          &amp;gt;&amp;lt;/TextInput&amp;gt;
          &amp;lt;TouchableOpacity onPress={onAdd} style={styles.addBtnBackground}&amp;gt;
            &amp;lt;Text style={styles.addBtnText}&amp;gt;Add&amp;lt;/Text&amp;gt;
          &amp;lt;/TouchableOpacity&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.todoListArea}&amp;gt;
          {list.map(({ value, checked, key }, idx) =&amp;gt; {
            return (
              &amp;lt;View style={styles.row} key={`${idx}-${key}`}&amp;gt;
                &amp;lt;View style={styles.left}&amp;gt;
                  &amp;lt;Checkbox
                    value={checked}
                    onValueChange={() =&amp;gt; {
                      onChangeCheck(idx, !checked);
                    }}
                  &amp;gt;&amp;lt;/Checkbox&amp;gt;
                  &amp;lt;Text
                    style={{
                      ...styles.todoText,
                      textDecorationLine: checked ? 'line-through' : 'none',
                    }}
                  &amp;gt;
                    {value}
                  &amp;lt;/Text&amp;gt;
                &amp;lt;/View&amp;gt;
                &amp;lt;TouchableOpacity
                  style={styles.deleteBtnBg}
                  onPress={() =&amp;gt; {
                    onDelete(idx);
                  }}
                &amp;gt;
                  &amp;lt;Text style={styles.deleteBtnText}&amp;gt;Delete&amp;lt;/Text&amp;gt;
                &amp;lt;/TouchableOpacity&amp;gt;
              &amp;lt;/View&amp;gt;
            );
          })}
        &amp;lt;/View&amp;gt;
      &amp;lt;/View&amp;gt;
    &amp;lt;/ViewTemplate&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 안드로이드, 오른쪽 IOS&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TodoApp.gif&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;880&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kB8xg/btsHS88WaDO/fKHON0IMtXWCuHMBnceHSk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kB8xg/btsHS88WaDO/fKHON0IMtXWCuHMBnceHSk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kB8xg/btsHS88WaDO/fKHON0IMtXWCuHMBnceHSk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/kB8xg/btsHS88WaDO/fKHON0IMtXWCuHMBnceHSk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;880&quot; data-filename=&quot;TodoApp.gif&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;880&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>front-end/react</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/242</guid>
      <comments>https://kmj24.tistory.com/242#entry242comment</comments>
      <pubDate>Sat, 8 Jun 2024 21:53:04 +0900</pubDate>
    </item>
    <item>
      <title>react-native 설치해보기</title>
      <link>https://kmj24.tistory.com/241</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 모바일 어플리케이션 개발에 대한 관심이 생겨 react-native를 사용해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모바일 어플리케이션을 개발하기 위해 여러가지 툴이 있지만, 나에게 가장 익숙한 코딩방식을 제공하는 react-native를 사용해보았다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;react-native란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;facebook (meta)에서 개발한 오픈소스 모바일 앱 프레임워크이다.&lt;/li&gt;
&lt;li&gt;프로그래밍 언어가 각각 다른 네이티브와 다르게(Java/Kotlin로 개발하는 Android, ObjectiveC/Swift의 IOS) JS 하나로 IOS, Android 개발이 가능한 크로스플랫폼이다.&lt;/li&gt;
&lt;li&gt;React와 비슷한 문법으로 React 개발자가 쉽게 입문하기 좋다는 특징이 있다.&lt;/li&gt;
&lt;li&gt;React와 동일한 문법을 사용하므로 React웹 개발자를 모바일 개발로 쉽게 이동 시킬 수 있다는 점, Android, IOS를 각각 개발해야 되는 노력이 적어지는 등 기업 입장에서 비용적으로 크게 매력적인 플랫폼이라고 볼 수 있다.&lt;/li&gt;
&lt;li&gt;네이티브 프레임워크보다 성능이 떨어진다는 단점이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 플랫폼에 대응해야 하므로 세밀한 최적화가 네이티브에 비해 제한적인 부분이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;react-native로 개발하기 위해 android와 ios를 모두 설치해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떠한 컴포넌트를 만들때 rn은 bridge를 통해 ios/android로 각각 컴포넌트를 만들게 하도록 한다.&lt;/li&gt;
&lt;li&gt;즉, rn이 직접 app을 만드는 것이 아닌, ios/android가 app을 만들도록 명령문을 날리는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설치&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react-native를 사용하기 위해 쉽게 설치할 수 있는 방법을 expo에서 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717845398708&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx create-expo-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react의 cra나 vite 템플릿과 같이 react-native를 개발할 수 있는 템플릿 코드를 제공하며, 이를 설치해 보다 쉽게 react-native를 개발할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;IOS 개발 환경 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;IOS를 개발하기 위해 XCode 가 필요하다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;돈을 모아서 mac을 사도록 하자.&lt;/s&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mac 환경에서는 XCode가 설치되어 있어야 한다.&lt;/li&gt;
&lt;li&gt;XCode를 설치했다면 IOS를 설치하여 IOS simulator를 사용할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2712&quot; data-origin-height=&quot;1772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yKwHO/btsHSzTvKC6/DruqpkFVU7XP6P471SUww0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yKwHO/btsHSzTvKC6/DruqpkFVU7XP6P471SUww0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yKwHO/btsHSzTvKC6/DruqpkFVU7XP6P471SUww0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyKwHO%2FbtsHSzTvKC6%2FDruqpkFVU7XP6P471SUww0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2712&quot; height=&quot;1772&quot; data-origin-width=&quot;2712&quot; data-origin-height=&quot;1772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android 개발 환경 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android를 설치하고 emulator를 설치한다.&lt;/li&gt;
&lt;li&gt;아래 링크를 따라 설치하면 개발환경을 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.expo.dev/workflow/android-studio-emulator/&quot;&gt;https://docs.expo.dev/workflow/android-studio-emulator/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1717845676162&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Studio Emulator&quot; data-og-description=&quot;Learn how to set up the Android Emulator to test your app on a virtual Android device.&quot; data-og-host=&quot;docs.expo.dev&quot; data-og-source-url=&quot;https://docs.expo.dev/workflow/android-studio-emulator/&quot; data-og-url=&quot;https://docs.expo.dev/workflow/android-studio-emulator/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1Nbrb/hyWg0QuR1I/SV813ty29BdV8bcQmTXxp1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bZ4Ni2/hyWlaYb4ag/Q42FtFOI5j1cQ8HiLSJhZ1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/cdISrj/hyWg2Ogg4Y/081DjoguxYWBziZZrmezJ0/img.png?width=620&amp;amp;height=620&amp;amp;face=0_0_620_620&quot;&gt;&lt;a href=&quot;https://docs.expo.dev/workflow/android-studio-emulator/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.expo.dev/workflow/android-studio-emulator/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1Nbrb/hyWg0QuR1I/SV813ty29BdV8bcQmTXxp1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/bZ4Ni2/hyWlaYb4ag/Q42FtFOI5j1cQ8HiLSJhZ1/img.png?width=1200&amp;amp;height=627&amp;amp;face=0_0_1200_627,https://scrap.kakaocdn.net/dn/cdISrj/hyWg2Ogg4Y/081DjoguxYWBziZZrmezJ0/img.png?width=620&amp;amp;height=620&amp;amp;face=0_0_620_620');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Android Studio Emulator&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how to set up the Android Emulator to test your app on a virtual Android device.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.expo.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3260&quot; data-origin-height=&quot;1774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6UBa8/btsHRw4D9c4/w0NwSnBZPPrfrxN5vARUVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6UBa8/btsHRw4D9c4/w0NwSnBZPPrfrxN5vARUVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6UBa8/btsHRw4D9c4/w0NwSnBZPPrfrxN5vARUVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6UBa8%2FbtsHRw4D9c4%2Fw0NwSnBZPPrfrxN5vARUVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3260&quot; height=&quot;1774&quot; data-origin-width=&quot;3260&quot; data-origin-height=&quot;1774&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>front-end/react</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/241</guid>
      <comments>https://kmj24.tistory.com/241#entry241comment</comments>
      <pubDate>Sat, 8 Jun 2024 20:22:16 +0900</pubDate>
    </item>
    <item>
      <title>JS 엔진 V8, 최적화 코드 작성</title>
      <link>https://kmj24.tistory.com/240</link>
      <description>&lt;h1&gt;자바스크립트 엔진&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터가 JS파일을 이해하고 명령을 수행할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;JS파일을 컴퓨터가 읽을 수 있는 코드로 변환하여 전달하여, 컴퓨터는 이를 실행한다.&lt;/li&gt;
&lt;li&gt;V8은 JS엔진의 종류 중 하나이며, 다양한 브라우저(크롬, 사파리, 엣지 등), 안드로이드 브라우저, NodsJS 런타임등 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Compile과 Interpret&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 언어를 컴퓨터가 읽을 수 있도록 번역을 하는 두가지 방식이 있다.&lt;/li&gt;
&lt;li&gt;Compilation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일러가 코드를 기계어로 번역한다.&lt;/li&gt;
&lt;li&gt;컴파일러는 코드 전체를 읽은 뒤 이를 바로 실행 가능한 기계어로 번역하여 실행한다.&lt;/li&gt;
&lt;li&gt;함수를 100번 실행 해야된다면 함수의 동작과정을 100번 실행하는 것이 아닌 함수의 결과를 100번 읽는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Interpretation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터프리터가 코드를 기계어로 번역한다.&lt;/li&gt;
&lt;li&gt;코드 한줄 한줄 읽어서 기계어로 번역한다.&lt;/li&gt;
&lt;li&gt;함수를 100번 실행해야 된다면 매번 함수를 한줄한줄 읽는 과정을 100번 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JS는 인터프리터가 코드를 한줄 한줄 읽는 방식으로 프로그램을 실행한다.&lt;/li&gt;
&lt;li&gt;인터프리터 특성상 코드가 많아질수록 속도가 느려진다는 단점이 있다.&lt;/li&gt;
&lt;li&gt;V8엔진은 이러한 JS의 수행 속도의 개선을 위해 설계되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;V8 엔진&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;V8엔진은 인터프리터를 사용하지 않고 JIT컴파일방식을 이용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JIT (Just In Time)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램 실행 시 코드의 일부 또는 전체를 런타임에 컴파일하는 기술이다.&lt;/li&gt;
&lt;li&gt;JIT 컴파일러는 인터프리터와 정적 컴파일러의 장점을 결합한 방식이다.&lt;/li&gt;
&lt;li&gt;특징
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;런타임 컴파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JIT 컴파일러는 프로그램이 실행되는 도중 코드를 컴파일 한다.&lt;/li&gt;
&lt;li&gt;프로그램이 실행되기 전에 코드 전체를 컴파일하는 정적 컴파일과는 다른 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;성능 최적화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램 실행 중 수집된 런타임 정보를 활용하여 최적화된 기계어 코드를 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;프로그램의 실행 성능을 향상시킬 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동작
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;코드를 토큰화 하여 AST(Abstract Syntax Tree, 추상 구문 트리)를 생성&lt;/li&gt;
&lt;li&gt;인터프리터를 사용하여 AST 기반 코드를 실행&lt;/li&gt;
&lt;li&gt;인터프리터는 코드 실행 과정 프로파일링 정보 수집&lt;/li&gt;
&lt;li&gt;프로파일링 정보를 기반으로 JIT 컴파일러가 IR(Intermediate Representation, 중간 표현)을 생성&lt;/li&gt;
&lt;li&gt;터보팬에서는 IR를 분석하여 최적화 시도&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인라인 캐싱&lt;/b&gt;, &lt;b&gt;히든 클래스&lt;/b&gt; 등 최적화&lt;/li&gt;
&lt;li&gt;최적화된 IR코드가 기계어로 컴파일 됨&lt;/li&gt;
&lt;li&gt;기계어를 실행함&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;히든클래스&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JS는 동적 언어이며 객체가 생성된 이후에도 속성을 쉽게 추가/삭제할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 언어는 메모리 offset(위치)를 컴파일 시 결정하며, 프로퍼티를 선언할 때 offset을 어딘가 저장해 둔 뒤 각 프로퍼티의 값이 필요할 때 offset의 값을 그대로 사용한다.&lt;/li&gt;
&lt;li&gt;동적언어는 특성상 객체에 접근할때 마다 조회(lookup, 해당 변수나 속성의 위치를 찾는 과정)과정을 거치므로 상대적으로 실행속도가 느려진다.&lt;/li&gt;
&lt;li&gt;히든 클래스는 이를 개선하기 위해 고안된 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;V8엔진은 히든 클래스를 활용하여 프로퍼티 구조를 효율적으로 처리한다.&lt;/li&gt;
&lt;li&gt;새로운 객체를 생성할 때 이에 대한 새로운 히든 클래스를 생성한다.&lt;/li&gt;
&lt;li&gt;그런 다음 새 프로퍼티를 추가해 동일한 객체를 수정하면 이전 클래스의 모든 프로퍼티가 포함된 새 히든클래스를 만들고 새 프로퍼티를 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717229932064&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Vector(x, y) {
  this.x = x; // B
  this.y = y; // C
}

const v = new Vector(1, 2); // A&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; A. V8엔진은 hc1이라는 히든 클래스를 생성하고 v 객체는 hc1을 참조한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;propertyTable: 프로퍼티가 메모리의 어디에 있는지 찾기 쉽게 알려줌&lt;/li&gt;
&lt;li&gt;transitionTable: 구조가 변하면 어디로 가야하는지 알려줌&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717229952824&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v: {}

// hidden class
hc1: {
	propertyTable: {},
	transitionTable: {},
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; B. V8엔진은 hc1을 참조하여 hc2라는 히든 클래스를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1717229967137&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v: { x: 1 }

// hidden class
hc1: {
	propertyTable: {},
	transitionTable: {
		property: &quot;x&quot;,
	},
},
hc2: {
	propertyTable: {
		property: &quot;x&quot;,
		offset: 1
	},
	transitionTable: {},
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; C. hc2를 참조하여 hc3라는 히든 클래스를 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1717230033872&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;v: { x: 1, y: 2 }

// hidden class
hc1: {
	propertyTable: {},
	transitionTable: {
		property: &quot;x&quot;
	},
},
hc2: {
	propertyTable: {
		property: &quot;y&quot;
	},
	transitionTable: {
		property: &quot;x&quot;,
		offset: 1
	},
},
h3: {
	propertyTable: {
		property: &quot;y&quot;,
		offset: 2
	},
	transitionTable: {
		property: &quot;x&quot;,
		offset: 1
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 과정을 통해 히든 클래스가 쌓인다.&lt;/li&gt;
&lt;li&gt;코드 작성시 히든 클래스가 생성될 때마다 기존에 생성된 히든 클래스를 사용하도록 고려해야 한다.&lt;/li&gt;
&lt;li&gt;아래의 코드는 히든 클래스를 고려했을 때 좋지 않은 방식이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717230059160&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Vector(x) {
  this.x = x;
}

const v1 = new Vector(1);
const v2 = new Vector(1);

v1.y = 2;
v1.z = 3;

v2.z = 3;
v2.y = 2;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v1, v2는 각각 동일한 데이터를 가지고 있지만 서로 다른 히든 클래스를 참조하게 된다.&lt;/li&gt;
&lt;li&gt;같은 offset을 참조하지 못하므로 캐싱이 되지 않고, 여러개의 히든 클래스를 만들어 각각 참조하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인라인 캐싱&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복문 내 객체 접근 시 조회 작업을 생략함으로써 성능을 향상시킨다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체의 요소에 접근하는 부분에 실제 메모리 주소를 할당하여 조회 과정을 생략한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;즉 객체의 구조가 동일해야 객체를 조회하는 과정을 생략하여 최적화 한다.&lt;/li&gt;
&lt;li&gt;예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래의 코드에서 객체들의 구조가 각각 다르다.&lt;/li&gt;
&lt;li&gt;이때 반복문을 통해 객체에 100억번 접근했을때 약 16초가 걸렸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1717230098434&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	(() =&amp;gt; {
  const minji = {
    firstName: 'Minji',
    lastName: 'Kim',
    job: 'singer',
  };
  const hanni = {
    firstName: 'Hanni',
    lastName: 'Pham',
    group: 'New jeans',
  };
  const daniel = {
    firstName: 'Daniel',
    lastName: 'Marsh',
    nationality: 'Australia',
  };
  const haerin = {
    firstName: 'Haerin',
    lastName: 'Kang',
    nickName: 'cat',
  };
  const hyein = {
    firstName: 'Hyein',
    lastName: 'Lee',
    mbti: 'ISFP',
  };

  const getFullName = (people) =&amp;gt; `${people.firstName} ${people.lastName}`;

  const people = [minji, hanni, daniel, haerin, hyein];

  const t0 = performance.now();
  for (let i = 0; i &amp;lt; 1000 * 1000 * 1000; i++) {
    getFullName(people[i % 5]);
  }
  const t1 = performance.now();

  console.log(`${Math.floor((t1 - t0) / 1000)}s`);
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 다르게 객체의 구조를 동일하게 구성하여 100번 접근했을 때 약 9초가 걸렸다.&lt;/p&gt;
&lt;pre id=&quot;code_1717230130780&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모두 동일하게 객체의 요소는 firstName, lastName으로만 구성되어 있다.
const minji = {
    firstName: 'Minji',
    lastName: 'Kim',
  };
  const hanni = {
    firstName: 'Hanni',
    lastName: 'Pham',
  };
  const daniel = {
    firstName: 'Daniel',
    lastName: 'Marsh',
  };
  const haerin = {
    firstName: 'Haerin',
    lastName: 'Kang',
  };
  const hyein = {
    firstName: 'Hyein',
    lastName: 'Lee',
  };&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;따라서 코드를 작성할 경우 아래의 내용을 고려하도록 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JS에서 코드를 작성할 때 항상 같은 순서로 초기화 하도록 한다.&lt;/li&gt;
&lt;li&gt;동일한 객체의 구조를 사용하여 인라인 캐싱이 되도록 한다.&lt;/li&gt;
&lt;li&gt;동일한 메서드를 반복적으로 수행하는 코드가 서로 다른 메소드를 한번씩 수행하는 것 보다 빠르게 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/240</guid>
      <comments>https://kmj24.tistory.com/240#entry240comment</comments>
      <pubDate>Sat, 1 Jun 2024 17:22:36 +0900</pubDate>
    </item>
    <item>
      <title>Web Worker</title>
      <link>https://kmj24.tistory.com/239</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;- Web worker를 이해하기 위해 프로세스와 스레드에 대하여 알고 있어야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로세스란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행중인 프로그램이다.&lt;/li&gt;
&lt;li&gt;즉, OS로부터 메모리를 할당받아 실행중인 프로그램을 의미한다.&lt;/li&gt;
&lt;li&gt;이러한 프로세스는 프로그램에 사용되는 데이터와 메모리, 스레드(Thread) 등으로 구성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스레드란?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스 내부에서 실행되는 작업 단위(unit)이다.&lt;/li&gt;
&lt;li&gt;하나의 프로세스는 내부에 여러개의 스레드가 포함될 수 있다.&lt;/li&gt;
&lt;li&gt;여러개의 스레드는 서로의 데이터에 접근이 가능하다.&lt;/li&gt;
&lt;li&gt;스레드도 프로세스처럼 작업을 병렬로 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;스레드의 메모리 영역을 thread stack이라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티 스레드&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개의 스레드를 구성하여 작업을 병렬로 처리한다.&lt;/li&gt;
&lt;li&gt;예를 들어 1분이 소요되는 4개의 작업을 직렬로 처리할 경우 4분이 소요된다.&lt;/li&gt;
&lt;li&gt;멀티스레딩으로 4개의 작업을 병렬로 처리한다면 1분으로 처리 시간을 단축할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JS에서의 스레드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JS는 이벤트 루프라는 싱글 스레드로 동작한다.&lt;/li&gt;
&lt;li&gt;코드를 한줄한줄 차례대로 실행하며 하나의 작업에 걸리는 시간과 관계없이 이전단계의 코드가 실행된 후 다음 단계의 코드가 실행된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 JS에서 비동기 처리를 제공한다. (Promise, Async/Await)&lt;/li&gt;
&lt;li&gt;이러한 비동기처리가 가능함으로 멀티스레드처럼 동작한다.&lt;/li&gt;
&lt;li&gt;이벤트 루프와 WebAPI의 메커니즘으로 비동기 처리가 가능하여 멀티스레드처럼 보일 수 있을 뿐 싱글스레드로 동작한다.&lt;/li&gt;
&lt;li&gt;이벤트루프에 대해서 : &lt;a href=&quot;https://kmj24.tistory.com/128&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kmj24.tistory.com/128&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저에서의 스레드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 다양한 스레드로 구성되어 있다.&lt;/li&gt;
&lt;li&gt;메인 스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저가 사용자 이벤트와 페인트를 처리하는 곳, JS가 실행되는 스레드이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;워커 스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JS를 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;Web worker, Service Worker 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컴포지터 스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 페이지의 레이아웃, 페인팅 작업을 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스토리지 스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저의 스토리지(local, session, cookie, indexedDB등) 브라우저 저장소에 접근하는 작업을 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네트워크 스레드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 요청을 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ETC..&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Web worker&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Web Worker 는 브라우저의 메인 스레드 와 별개로 작동되는 스레드이다&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Web worker를 이용한 브라우저 멀티 스레딩을 사용한 예시&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;canvas를 이용하여 차트를 개발한 경험이 있다.&lt;/li&gt;
&lt;li&gt;canvas는 화면에 다시 그릴 때 canvas의 영역을 전체 지우고 다시 그리는 방식으로 다시 그린다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 canvas에 인터렉티브한 요소를 사용한다면 화면을 다시 그리기 위한 전체 연산을 해야한다.&lt;/li&gt;
&lt;li&gt;인터렉티브한 요소가 작다면(다시 그릴 필요가 없는 요소가 많은) 그만큼 비효율적인 연산이 실행되는 것이다.&lt;/li&gt;
&lt;li&gt;canvas에서는 이를 위해 최적화 방안이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개의 canvas를 겹쳐두어 정적 요소에 대한 canvas, 동적 요소에 대한 canvas를 따로 그려내어 동적 요소만 다시그리도록 하는것.&lt;/li&gt;
&lt;li&gt;Path2D를 사용하여 그릴 경로를 미리 정해두고, canvas에 직접적으로 무엇인가를 지시하는 횟수를 줄이는 것&lt;/li&gt;
&lt;li&gt;하지만 이 방법만으로 충분하지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;브라우저는 Web Worker라는 스레드를 제공하며, 멀티스레딩을 이용한 canvas의 연산을 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Offscreen Canvas
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;canvas를 그리기 위해 대량의 연산을 실행 할 때 메인스레드만을 이용한다면 그만큼의 지연이 발생할 것이다.&lt;/li&gt;
&lt;li&gt;이때 메인 스레드가 아닌 백그라운드 스레드에서 canvas를 다룬다면, 그만큼의 지연을 줄일수 있다.&lt;/li&gt;
&lt;li&gt;OffscreenCanvas는 Main thread가 아닌 Web worker에서 렌더링하여 지연에 대응하므로 더 좋은 UX를 제공할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Offscreen Canvas 예시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검은 공이 canvas 왼쪽에서 오른쪽으로 이동하는 애니메이션이 있다.&lt;/li&gt;
&lt;li&gt;이때 main thread에 강제로 부하를 걸었을 때, 일반 canvas와 offscreen canvas의 차이를 비교해 볼것이다.&lt;/li&gt;
&lt;li&gt;animation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1716708852963&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const animation = (callback: () =&amp;gt; void) =&amp;gt; {
	callback();
	requestAnimationFrame(animation);
}

const draw = (ctx: CanvasRenderingContext2D) =&amp;gt; {
  let xPos = 10;

  return () =&amp;gt; {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.beginPath();

    ctx.arc(xPos, 100, 10, 0, Math.PI * 2, false);
    ctx.fill();

    xPos += 3;
		
		// canvas의 width는 1200
    if (xPos &amp;gt; 1200) {
      xPos = 0;
    }
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 canvas&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1716708913880&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const canvas = document.querySelector('.canvas');
const ctx = canvas.getContext('2d');
animation(draw(ctx));&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;offscreen canvas&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;offsecreen canvas는 canvas와 다르게 worker를 생성하여 offscreen canvas를 넘겨준다.&lt;/li&gt;
&lt;li&gt;애니메이션 렌더링은 워커에서 실행하도록 하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1716708938245&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const canvas = document.querySelector('.canvas');
const offscreen = canvas.transferControlToOffscreen();

const worker = new Worker('worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);

// worker.js
import { draw } from './draw';

const worker = self;
worker.onmessage = (e) =&amp;gt; {
  const { canvas } = e.data;
  const ctx = canvas.getContext('2d')!;

  animation(draw(ctx));
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메인스레드의 부하&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3초간 main thread가 while문 내에 머물도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1716708998686&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 3초간 main thread를 blocking
const blockThread = () =&amp;gt; {
  const start = Date.now();
  while (Date.now() - start &amp;lt; 3000) {}
};&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부하가 일어나면 canvas는 렌더링을 멈춘다. 반면 worker thread에서 실행되는 offscreen canvas는 렌더링을 멈추지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;offscreenCanvasExample.gif&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vpkGx/btsHDA5Bii5/qxSflcaaSYXg6Rwb8IGYA0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vpkGx/btsHDA5Bii5/qxSflcaaSYXg6Rwb8IGYA0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vpkGx/btsHDA5Bii5/qxSflcaaSYXg6Rwb8IGYA0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/vpkGx/btsHDA5Bii5/qxSflcaaSYXg6Rwb8IGYA0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1722&quot; height=&quot;654&quot; data-filename=&quot;offscreenCanvasExample.gif&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/239</guid>
      <comments>https://kmj24.tistory.com/239#entry239comment</comments>
      <pubDate>Sun, 26 May 2024 16:40:52 +0900</pubDate>
    </item>
    <item>
      <title>Monorepo를 버리기로 했다</title>
      <link>https://kmj24.tistory.com/238</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Monorepo를 도입하게 된 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 재직중인 회사에서 yarn workspace를 사용한 Monorpo로 프론트 시스템을 구축했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Monorepo를 도입하며 아래의 효과를 기대했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;중복되는 패키지에 대한 활용도 향상&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 ESlint 또는 Prettier와 같이 각 패키지별 동일한 설정이 필요한 경우, Root 패키지에 한번만 설정해두면 모든 하위 프로젝트에서 동일하게 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- rollup이나 webpack과 같은 번들러를 사용하기 위한 종속성을 Root에 설치하면 각 앱별로 동일한 종속성을 바라보게 되므로 한번의 설치로 각 프로젝트에 번들러를 적용할 수 있고, 프로젝트의 절대적인 크기는 줄어들게된다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컴포넌트 재사용성을 Application(이하 앱) 단위가 아닌 프로젝트 단위로 활용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ui를 제공하는 컴포넌트를 다른 프로젝트에 개발한 후 앱에서 사용할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ui를 개발하는 패키지를 Workspace 내부에 개발하여 빌드해두고, 여러 다른 앱에서 해당 컴포넌트를 동일하게 사용한다면 개발 생산성 향상 및 디자인이 일관적이게 될 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발생한 문제점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Monorepo를 이용하여 패키지 별 의존성이 같은 것으로 연결된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 22년 말 노드 버전을 업데이트 하지 않으면 안되는 상황이 발생했다. Monorepo의 구축 당시 node의 버전은 14버전을 활용했고, 14버전은 23년 4월을 마지막으로 EOL(End-Of-Life)가 되었다. nodejs의 공식적인 지원이 종료되는 EOL은 보안취약점에 대한 패치가 없고 호환성도 떨어지게 되며, 새로운 라이브러리가 필요할 때 큰 제약사항이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이에 따라 node 버전을 업데이트 하는 과정을 거쳤는데 모든 앱에 걸쳐 맞물려 있는 버전 의존성 때문에 수많은 오류를 만나게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각각의 앱에서 사용하는 라이브러리의 업데이트도 골치인데, 공통 라이브러리의 업데이트는 더 골치인 상황이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 어떤 앱에서 발생한 문제점에 대한 해결을 할때마다 다른 앱에서 사이드 이펙트가 나타났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어, 3개의 앱에서 사용하는 어떤 라이브러리에서 오류가 발생했다고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;1. 그 중 한개의 앱에서 발생하는 오류를 해결하기 위한 조치를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;2. 그리고 다른 앱에서 발생하는 오류를 해결하기 위한 조치를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;3. 1번에서 해결한 문제가 2번에 대한 조치가 끝나고 1번 앱에서 다른 문제가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;4. 3번 문제를 해결하기 위해 두 앱이 모두 오류가 나지 않도록 조치를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;5. 3번째  앱에서 발생한 오류를 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;6. 3번째 앱에서 발생한 오류를 해결하니 1번, 2번 앱에서 다시 오류가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이러한 오류 하나하나를 해결해나가는데 쓸데없는 시간낭비가 크게 발생했고, 개발에 병목현상이 생겨 Monorepo를 활용하기 어렵다고 판단이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 또한 yarn berry를 도입하게 되었는데 이는 프로젝트 별 root의 종속성을 바라보지 않는다. 따라서 공통 종속성을 사용하기 위한 Monorepo를 사용할 필요가 없어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주니어 개발자들로 이루어진 팀에서의 Monorepo 활용이 어렵고,(같이 참여한 개발자의 퇴사로 Monorepo를 어느정도 이해하고 활용할 수 있는 개발자는 나 하나 뿐이었다.) 소규모 프로젝트에서는 굳이 Monorepo를 사용할 필요가 없다고 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 따라서 신규 프로젝트에서는 Monorepo에 포함 시키지 않도록하였다. 그리고 Monorepo에 포함된 몇몇 앱들이 너무 레거시하다고 판단하여 버전을 픽스 해두고 새로운 버전으로 앱을 재개발하기로 함에 따라 Monorepo를 더이상 키우지 않기로 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>front-end</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/238</guid>
      <comments>https://kmj24.tistory.com/238#entry238comment</comments>
      <pubDate>Sun, 14 May 2023 13:59:17 +0900</pubDate>
    </item>
    <item>
      <title>useTrie 문자열 자동 완성 hook</title>
      <link>https://kmj24.tistory.com/237</link>
      <description>&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Select 컴포넌트를 만들다가 자동완성 기능이 필요했다.&lt;/li&gt;
&lt;li&gt;자동완성을 위한 알고즘이 어떤것이 있을까 하다가 Trie DS를 이용하여 간단하게 구현할 수 있을 것 같아서 구현을 하게 되었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;trie란&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;Trie란?&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열의 접두어 기준으로 트리 형태로 저장하는 자료구조&lt;/li&gt;
&lt;li&gt;dictionary 타입으로 이루어진 트리 형태가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bv7jI1/btsdQKXhGFt/6ViL8YtKyX3pnfdb0ByQGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bv7jI1/btsdQKXhGFt/6ViL8YtKyX3pnfdb0ByQGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bv7jI1/btsdQKXhGFt/6ViL8YtKyX3pnfdb0ByQGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbv7jI1%2FbtsdQKXhGFt%2F6ViL8YtKyX3pnfdb0ByQGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;611&quot; height=&quot;504&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Prefix&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- dictionary의 key&lt;/li&gt;
&lt;li&gt;&lt;b&gt;isWord&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 해당 노드가 마지막 깊이의 노드인지, 즉 문자열의 끝인지 여부를 가짐, boolean type&lt;/li&gt;
&lt;li&gt;&lt;b&gt;word&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 해당 노드가 마지막 깊이의 노드일 경우 prefix로 시작하는 전체 문자열을 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;next&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 다음 노드를 가리키는 참조자&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;기능&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;기능&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;필요한 모든 문자열을 Trie DS로 생성한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 문자열이 여러개가 올 수 있으며,&lt;/li&gt;
&lt;li&gt;문자열에 대한 추가적인 옵션도 저장할 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;한글도 자동완성이 되어야 하며, 이를 위해 한글이 입력되면 음소로 쪼개어 저장한다.&lt;/li&gt;
&lt;li&gt;input으로 입력받은 문자가 포함된 모든 문자열이 리스트에 출력되도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;구현&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;구현&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;class를 이용하여 구현해보았다.&lt;/li&gt;
&lt;li&gt;먼저 Trie Node를 정의하며 각각의 노드가 다음의 노드를 가리키는 형태가 된다
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;class TrieNode {
  public isWord: boolean;

  public next: { [key: string]: TrieNode };

  public info: Array&amp;lt;TrieData&amp;gt; | null;

  constructor() {
    this.isWord = false;

    this.next = {};

    this.info = null;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;멤버 변수 info의 타입인 TrieData는 같은 문자열에 대한 구분을 위해 key 그리고 추가적인 데이터를 위한 option도 포함하는 타입이다.
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;type TrieData = {
  label: string;
  key: string | number;
  options?: { [key: string]: any };
};&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;입력받은 문자열을 포함하는 데이터를 모두 찾아서 배열로 반환한다.
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;private extractStr = (str: string) =&amp;gt; {
  const cur = [];
  for (let i = 0; i &amp;lt; str.length; i++) {
    const c = str[i];
    if (Hangul.isHangul(c)) {
      const extract = Hangul.make(str[i]);
      cur.push(...extract.split(''));
    } else {
      cur.push(str[i]);
    }
  }

  return cur;
};

public containList = (input: string): TrieData[] =&amp;gt; {
  if (!input || input.length === 0) return [];
  const containList: TrieData[] = [];
  const extractInput = this.extractStr(input).join('');

  const recursion = (node: TrieNode) =&amp;gt; {
    if (node === undefined) return;
    if (node.isWord &amp;amp;&amp;amp; node.info) {
      node.info.forEach((val: TrieData) =&amp;gt; {
        const extractLabel = this.extractStr(val.label).join('');
        if (extractLabel.includes(extractInput)) {
          containList.push(val);
        }
      });
    }
    Object.values(node.next).forEach((n) =&amp;gt; {
      recursion(n);
    });
  };

  recursion(this.root);

  return containList;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;extractStr을 이용하여 문자(음소)를 배열 형태로 추출하고 추출된 문자를 trie에서 찾는다.&lt;/li&gt;
&lt;li&gt;찾은 문자를 리스트로 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;usetrie&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;useTrie&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useTrie 코드는 다음과 같다.
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { useMemo, useEffect } from 'react';

import Trie from './Trie';
import Hangul from './Hangul';

import type { TrieData } from './types';

/**
 * trie Data structure를 생성
 * @param dictionary trie 생성 데이터
 * @param isBuildTrie trie 생성 여부
 * @returns
 */
function useTrie(dictionary: Array&amp;lt;TrieData&amp;gt;, isBuildTrie: boolean = true) {
  const trie = useMemo(() =&amp;gt; new Trie(), []);

  useEffect(() =&amp;gt; {
    if (isBuildTrie &amp;amp;&amp;amp; trie.isDiff(dictionary)) {
      trie.initialize();
      dictionary.forEach((val: TrieData) =&amp;gt; {
        const extract = Hangul.make(val.label);
        trie.insert(extract, val);
      });
    }
  }, [trie, dictionary, isBuildTrie]);

  return trie;
}

export default useTrie;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Trie class를 이용하여 trie를 생성하며 생성된 trie를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;사용&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;사용&lt;/h1&gt;
&lt;pre class=&quot;dockerfile&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;const trie = useTrie(
    [
			{
				key: 0,
				label: '하나',
			},
			{
				key: 1,
				label: '하나',
			},
			{
				key: 2,
				label: '한다',
			},
			{
				key: 3,
				label: '한글',
			},
		]
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9INoQ/btsdOSotuAw/rCk7APL76nEsKWjwJO0wrK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9INoQ/btsdOSotuAw/rCk7APL76nEsKWjwJO0wrK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9INoQ/btsdOSotuAw/rCk7APL76nEsKWjwJO0wrK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/9INoQ/btsdOSotuAw/rCk7APL76nEsKWjwJO0wrK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;195&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;195&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>front-end</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/237</guid>
      <comments>https://kmj24.tistory.com/237#entry237comment</comments>
      <pubDate>Wed, 3 May 2023 21:48:50 +0900</pubDate>
    </item>
    <item>
      <title>React query</title>
      <link>https://kmj24.tistory.com/236</link>
      <description>&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React Application에서 서버 상태 관리(Caching, Continous Synchronization, etc&amp;hellip;)의 도움을 주는 라이브러리이다.&lt;/li&gt;
&lt;li&gt;api요청을 보낼 때, React의 Local state와 Server state를 동기화하게 되는데 이때 편의성을 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;client-state-vs-server-state&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Client state vs Server state&lt;/h2&gt;
&lt;h3 id=&quot;client-state&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Client state&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 소유&lt;/li&gt;
&lt;li&gt;클라이언트에서 제약없는 제어가 가능함&lt;/li&gt;
&lt;li&gt;Client내 UI/UX 흐름 또는 사용자 인터렉션에 따라 변할 수 있음&lt;/li&gt;
&lt;li&gt;Client내에서 최신 상태로 관리됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;server-state&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Server state&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버의 상태, 클라이언트 입장에서는 원격의 공간에서 제어되는 상태&lt;/li&gt;
&lt;li&gt;Fetching / Updating에 비동기 API가 필요&lt;/li&gt;
&lt;li&gt;다른 클라이언트들과 공유되며, 클라이언트가 모르는 사이 변경될 수 있음&lt;/li&gt;
&lt;li&gt;신경쓰지 않을 경우, out of date가 될 잠재적인 가능성이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단순-api-request&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;단순 API request&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단순한 요청&lt;/b&gt;, XMLHttpRequest, fetch, axios 등을 실행한 요청
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React 컴포넌트 내에서 서버 상태를 동기화 하게 되며 코드가 장황해짐(state drilling, loading 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전역 상태 관리 시스템을 이용한 API&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 상태를 React 컴포넌트 외부에서 관리&lt;/li&gt;
&lt;li&gt;redux를 사용할 경우 redux 특징인 작성되는 코드가 길어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React-query&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상대적으로 간결한 문법을 제공&lt;/li&gt;
&lt;li&gt;각종 옵션으로 편의성을 제공(interval polling, Garbage collection 설정 등)&lt;/li&gt;
&lt;li&gt;Caching으로 빠른 성능 제공&lt;/li&gt;
&lt;li&gt;불필요한 다중 요청 방지 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;react-query의-컨셉&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;react-query의 컨셉&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RFC 5861 (Request for Comments - 인터넷 개발에 필요한 기술, 결과, 절차 등 메모해놓은 문서)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP Cache-Control Extensions for Stale Content&lt;/li&gt;
&lt;li&gt;stale-while-revalidate
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백그라운드에서 stale response(오래된 응답)를 revalidate(refetch)하는 동안 캐시가 가진 stale response(이전 응답, 오래된 응답)를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cache-Control header
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Cache-Control: max-age=600, stale-while-revalidate=30&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;600s가 넘어갈 경우 해당 데이터는 stale(오래 되었다)판단&lt;/li&gt;
&lt;li&gt;600s ~ 30s refetch한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Latency&lt;/b&gt;가 숨겨지는 이점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 컨셉을 메모리 캐시에 적용 시도
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과물 : react-query, swr, &amp;hellip;&lt;/li&gt;
&lt;li&gt;react-query에서,,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;cacheTime&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 데이터가 메모리에 얼마 동안 존재할 것인가(해당 시간 이후 GC가 제거, default 5분)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;staleTime&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 얼마의 시간이 흐른 후 데이터를 stale 취급할 것인가 (default 0)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;react-query stale 옵션
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;refetchOnMount&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: mount시점에 데이터를 stale 취급&lt;/li&gt;
&lt;li&gt;&lt;b&gt;refetchOnWindowFocus&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: window의 focus 시점에 데이터를 stale 취급&lt;/li&gt;
&lt;li&gt;&lt;b&gt;refetchOnReconnect&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: reconnect 시점에 데이터를 stale 취급&lt;/li&gt;
&lt;li&gt;stale이라 판단되면 refetch (default : true)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-life-cycle&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Query life-cycle&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t4zFK/btsdQJqBlnU/azBnXg3Ftg0MP6pK4kxtE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t4zFK/btsdQJqBlnU/azBnXg3Ftg0MP6pK4kxtE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t4zFK/btsdQJqBlnU/azBnXg3Ftg0MP6pK4kxtE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft4zFK%2FbtsdQJqBlnU%2FazBnXg3Ftg0MP6pK4kxtE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;377&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;query 인스턴스가 mount됨&lt;/li&gt;
&lt;li&gt;stale time이 0보다 크다면 fresh상태&lt;/li&gt;
&lt;li&gt;stale time만료 시 stale 상태가 됨&lt;/li&gt;
&lt;li&gt;스크린에서 사용되는 동안 stale 상태는 지속된다.&lt;/li&gt;
&lt;li&gt;스크린에서 사용되지 않는다면 query는 inactive 상태가 됨&lt;/li&gt;
&lt;li&gt;cache time이 만료되기 전까지 메모리 상에서 존재함&lt;/li&gt;
&lt;li&gt;cache time이 만료될 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GC&lt;/b&gt;가 제거&lt;/li&gt;
&lt;li&gt;inactive 상태였다가 다시 스크린에 나타나야 될 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;refetchOnMount&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태가 되며, refetch 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;react-query-default-config&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;react-query default config&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Queries에서 Cached data는 언제나 stale 취급&lt;/li&gt;
&lt;li&gt;각 시점에서 data가 stale이라면 항상 refetch 발생&lt;/li&gt;
&lt;li&gt;Inactive query들은 5분 뒤&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;GC&lt;/b&gt;가 제거&lt;/li&gt;
&lt;li&gt;Query 실패 시 최대 3번 retry 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;react-query-데이터&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;react-query 데이터&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;stale time은 30s로 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트A&lt;/b&gt;에서 api-1을 호출&lt;/li&gt;
&lt;li&gt;10초뒤&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;컴포넌트B&lt;/b&gt;에서 api-1을 호출&lt;/li&gt;
&lt;li&gt;같은 api-1을 호출하지만 컴포넌트B에서 api-1호출은 실행되지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Context API&lt;/b&gt;를 활용한 전역상태로 관리되는 데이터
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;QueryClient 내부적으로 Context API를 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;react-query-사용-방법&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;react-query 사용 방법&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application에서 사용할 부분에 QueryClientProvider를 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { QueryCLient, QueryClientProvider } from 'react-query';

const queryClient = new QueryClient();

function App() {
	return &amp;lt;QueryClientProvider client={queryClient}&amp;gt;...&amp;lt;/QueryClientProvider&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usequery&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;useQuery&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 읽을 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-key&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Query Key&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { useQuery } from 'react-query';

function App() {
	// useQuery(query key, ...);
	const info = useQuery(**query key**, fetcher);

	//...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;react query는 query key에 따라 캐싱을 관리한다.&lt;/li&gt;
&lt;li&gt;데이터는 query key와 매핑된다.&lt;/li&gt;
&lt;li&gt;데이터 타입은 string, Array이 될 수 있으며, react-query v4(beta)부터는 array 타입만 허용한다고 한다.&lt;/li&gt;
&lt;li&gt;query key는 해싱 되며, 아래의 키는 모두 같은 것으로 간주된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;useQuery(['todos', { status, page }], ...)
useQuery(['todos', { page, status }], ...)
useQuery(['todos', { page, status, other: undefined }], ...)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 아래의 형태는 같은것으로 간주되지 않는다. (배열의 순서가 중요)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;useQuery(['todos', status, page], ...)
useQuery(['todos', page, status], ...)
useQuery(['todos', undefined, page, status], ...)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;query-function&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Query function&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { useQuery } from 'react-query';

function App() {
	// useQuery(query key, query function);
	const info = useQuery(query key, **fetcher**);

	//...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;promise를 반환하는 함수&lt;/li&gt;
&lt;li&gt;api fetch함수가 들어간다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-options&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Query Options&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;import { useQuery } from 'react-query';

function App() {
	// useQuery(query key, query function, query option);
	const info = useQuery(query key, fetcher, **option**);

	//...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useQuery를 사용할 때 옵션 설정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;onSuccess, onError, onSettled&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입(순서대로) : (&lt;b&gt;data: TData) &amp;rArr; void, (error: TError) &amp;rArr; void, (data?: TData, error?: TError) &amp;rArr; void&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;query fetching 성공 실패 완료 시 실행할 side effect 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;enabled&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 : boolean&lt;/li&gt;
&lt;li&gt;자동으로 query를 실행 할지 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;retry&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;boolean | number | (failureCount: number, error: TError) &amp;rArr; boolean&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;실패한 쿼리 재시도 옵션&lt;/li&gt;
&lt;li&gt;&lt;b&gt;true&lt;/b&gt;설정 시 무한 재시도,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;false&lt;/b&gt;설정 시 재시도x&lt;/li&gt;
&lt;li&gt;default = 3번&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;select&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(data: TData) =&amp;gt; unknown&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;성공 시 가져온 데이터 가공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;keepPreviousData&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;boolean&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;새롭게 fetching시 이전 데이터 유지 여부&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;refetchInterval&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;number | false | ((data: TData | undefined, query: Query) =&amp;gt; number | false)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;polling 사용 여부&lt;/li&gt;
&lt;li&gt;number타입 설정 시 해당 쿼리가 ms 단위로 refetch&lt;/li&gt;
&lt;li&gt;function설정시 최신 데이터로 함수가 실행, 빈도를 계산하는 쿼리 실행&lt;/li&gt;
&lt;li&gt;default = false&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;staleTime (number | Infinity)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터가 fresh상태로 유지되는 시간 (fresh상태일 경우 쿼리가 다시 마운트 되어도 fetch는 실행되지 않음)&lt;/li&gt;
&lt;li&gt;해당 시간 초과 시 stale 상태로 변경됨&lt;/li&gt;
&lt;li&gt;default = 0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;etc..&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;usemutation&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;useMutation&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 업데이트 할때 사용(post, put, delete)&lt;/li&gt;
&lt;li&gt;useQuery는 즉시 실행되지만, useMutation은 즉시 실행되지 않는다.&lt;/li&gt;
&lt;li&gt;useQuery는 상태를 공유하지만, useMutation은 그렇지 않다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useQuery는 다른 컴포넌트에서 동일한 useQuery를 여러번 호출할 수 있으며, 캐시된 결과를 동일하게 반환하지만, useMutation에는 그런 기능이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sas&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;const { data, error, isError, isLoading, mutate, mutateAsync, reset ... } = 
	useMutation(mutationFunction, options);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반환 값
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;mutate&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutation을 실행하는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mutateAsync&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutate와 비슷하지만 promise를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;reset&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutation내부 상태 clear&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;options
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;onMutate&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 타입 : (variables) &amp;rArr; Promise&amp;lt;TContext | void&amp;gt; | TContext void&lt;/li&gt;
&lt;li&gt;본격적인 Mutation동작 전에 먼저 동작하는 함수&lt;/li&gt;
&lt;li&gt;Optimistic update 적용시 유용함&lt;/li&gt;
&lt;li&gt;Optimistic update &lt;span style=&quot;color: #000000;&quot;&gt;-&lt;/span&gt; 낙관적 업데이트 &lt;span style=&quot;color: #000000;&quot;&gt;-&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;UI&lt;/span&gt;에서 어차피 업데이트 할 것이란 가정으로 &lt;span style=&quot;color: #000000;&quot;&gt;UI&lt;/span&gt;를 업데이트 후 서버를 통한 검증 &lt;span style=&quot;color: #000000;&quot;&gt;-&lt;/span&gt; 실패한다면 롤백&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;onMutateAsync&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;onMutate의 비동기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;etc&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-invalidation&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Query Invalidation&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;queryClient를 통해 invalidate 메서드 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;sql&quot; style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;code&gt;// Invalidate every query in the cache
queryClient.invalidateQueries()

// Invalidate every query with a key that starts with 'todos'
queryClient.invalidateQueries('todos')&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 key를 가진 query는 stale 취급&lt;/li&gt;
&lt;li&gt;현재 rendering되고 있는 query는 백그라운드에서 refetch된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-파일-분리&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;query 파일 분리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트에서 직접 useQuery를 호출한다면 관리의 어려움이 있을 수 있다.&lt;/li&gt;
&lt;li&gt;query fetching역할의 파일을 따로 분리하는것 권장한다.&lt;/li&gt;
&lt;li&gt;배민 주문시스템의 웹뷰에서 데이터 패칭을 할 때 도메인 별로 묶어서 관리한다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;query-상태&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;query 상태&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fetching : 데이터 요청 상태&lt;/li&gt;
&lt;li&gt;fresh : 데이터가 만료되지 않은 상태&lt;/li&gt;
&lt;li&gt;stale : 데이터가 만료된 상태&lt;/li&gt;
&lt;li&gt;inactive : 사용하지 않는 상태&lt;/li&gt;
&lt;li&gt;delete : GC에 의하여 Cache에서 제거된 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;react-query-사용상-장점&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;react-query 사용상 장점&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 상태 관리 용이 (redux, mobx보다 직관적인 API 호출, Caching 으로 빠른 성능 지원)&lt;/li&gt;
&lt;li&gt;API 처리에 대한 각종 인터페이스, 옵션 제공&lt;/li&gt;
&lt;li&gt;Client store는 클라이언트의 상태만 남아서 store답게 사용됨&lt;/li&gt;
&lt;li&gt;Cache전략 사용 시 효율적&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>front-end/react</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/236</guid>
      <comments>https://kmj24.tistory.com/236#entry236comment</comments>
      <pubDate>Wed, 3 May 2023 21:48:25 +0900</pubDate>
    </item>
    <item>
      <title>HTML Canvas 최적화</title>
      <link>https://kmj24.tistory.com/235</link>
      <description>&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;canvas 2d context 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2d context 생성 시 alpha(투명도) 옵션을 false로 생성&lt;/li&gt;
&lt;li&gt;context alpha 채널이 포함되지 않도록 하여, 브라우저는 배경이 항상 불투명하다고 인식하므로, 렌더링 속도가 개선&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;const&lt;/span&gt; context &lt;span style=&quot;color: #000000;&quot;&gt;=&lt;/span&gt; canvas&lt;span style=&quot;color: #000000;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;getContext&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;'2d'&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;{&lt;/span&gt; alpha&lt;span style=&quot;color: #000000;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;false&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;안티 앨리어싱&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴퓨터 그래픽 해상도의 한계로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;계단 현상&lt;/b&gt;이 발생하는 앨리어싱이 발생&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;DPR&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;(&lt;/span&gt;Device Pixel Ratio&lt;span style=&quot;color: #000000;&quot;&gt;)&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;:&lt;/span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;CSS&lt;/span&gt; Pixel을 구성하는데 필요한 물리적 Pixel 수&lt;/li&gt;
&lt;li&gt;ex) 해상도가 높은 모니터에서 text가 흐릿하게 보이는 text blurry 현상이 발생됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DPR의 기본값은 1, 레티나 디스플레이의 DPR은 2인데, 이때 디스플레이마다 CSS 1픽셀을 그리는데 사용되는 실제 픽셀수가 달라짐&lt;/li&gt;
&lt;li&gt;canvas의 크기를 DPR에 따라 보정이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;canvas 라인 블러 현상 보정&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lineWidth 가 홀수일 경우 라인이 흐릿하게 퍼지는 블러 현상이 발생됨&lt;/li&gt;
&lt;li&gt;라인을 그릴때 좌표와 두께를 기준으로 라인을 그리기 때문에 발생되는 현상&lt;/li&gt;
&lt;li&gt;(3, 1)에서 (3, 5)로 두께가 1인 라인을 그릴 때, 라인은 중심이 되는 지점부터 그려지므로 두께가 1이지만 그리드에서 2칸을 차지하므로 블러 현상이 발생&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blr7cI/btsdPvNl9CA/p0GqV9tYWneHPAbmhxES70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blr7cI/btsdPvNl9CA/p0GqV9tYWneHPAbmhxES70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blr7cI/btsdPvNl9CA/p0GqV9tYWneHPAbmhxES70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblr7cI%2FbtsdPvNl9CA%2Fp0GqV9tYWneHPAbmhxES70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;944&quot; height=&quot;316&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;라인의 두께가 홀수일 경우 발생되는 현상이므로, 라인 너비가 홀수이면, 반 픽셀 이동하여 블러 현상을 보정하여 선명한 라인 표현&lt;/li&gt;
&lt;li&gt;보정 전&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;100&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGinbC/btsdOA9vefM/3Y6Me5DNmB7aQbXCmTSzN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGinbC/btsdOA9vefM/3Y6Me5DNmB7aQbXCmTSzN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGinbC/btsdOA9vefM/3Y6Me5DNmB7aQbXCmTSzN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGinbC%2FbtsdOA9vefM%2F3Y6Me5DNmB7aQbXCmTSzN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;100&quot; height=&quot;574&quot; data-origin-width=&quot;100&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;보정 후&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RVRbd/btsdQf4k2uC/b7vZ3viEpLeXcvsYfI1JY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RVRbd/btsdQf4k2uC/b7vZ3viEpLeXcvsYfI1JY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RVRbd/btsdQf4k2uC/b7vZ3viEpLeXcvsYfI1JY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRVRbd%2FbtsdQf4k2uC%2Fb7vZ3viEpLeXcvsYfI1JY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;130&quot; height=&quot;566&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;canvas layer&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;canvas의 animation을 표현하기 위해 canvas에 그려진 화면을 지우고 다시 그리는 과정이 필요한데 만약 데이터량이 클수록 재연산이 발생하므로 성능저하가 심해짐&lt;/li&gt;
&lt;li&gt;animation 표현을 담당하는 canvas, 정적 표현을 담당하는 canvas등으로 역할을 분리한 canvas를 여러장 겹쳐 정적 데이터 재연산을 skip&lt;/li&gt;
&lt;li&gt;시간복잡도 단축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n &amp;rarr; data 량&lt;/li&gt;
&lt;li&gt;m &amp;rarr; animation 발생 빈도&lt;/li&gt;
&lt;li&gt;개선 전 O(n*m) &amp;rarr; 개선 후 O(n)&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chT0lq/btsdQga6sSI/LXKAk4K63GdsSmu8cM4zSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chT0lq/btsdQga6sSI/LXKAk4K63GdsSmu8cM4zSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chT0lq/btsdQga6sSI/LXKAk4K63GdsSmu8cM4zSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchT0lq%2FbtsdQga6sSI%2FLXKAk4K63GdsSmu8cM4zSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;390&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>front-end/HTML5 canvas</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/235</guid>
      <comments>https://kmj24.tistory.com/235#entry235comment</comments>
      <pubDate>Wed, 3 May 2023 21:47:50 +0900</pubDate>
    </item>
    <item>
      <title>디자인 시스템을 위한 UI 컴포넌트 커스텀 색상 주입 (테마 설정)</title>
      <link>https://kmj24.tistory.com/234</link>
      <description>&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Problem&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디자인 시스템 구축을 위한 UI 프로젝트의 개발을 진행.&lt;/li&gt;
&lt;li&gt;최초 개발 시 컴포넌트별 색상의 변경이 있을 것이라는 고려는 없는 상태로 개발 진행하여 version1에서 지원해야 할 컴포넌트들이 개발됨.&lt;/li&gt;
&lt;li&gt;light 테마만 지원하는 프로젝트가 개발됨&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Use case&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;신규 개발 프로젝트에서 디자인 컴포넌트를 제공하기 위해 개발 하던 도중 고객사의 요청이 다크 테마를 지원해야 된다고 하여 디자인이 변경됨&lt;/li&gt;
&lt;li&gt;따라서 기존에 개발된 컴포넌트의 색상때문에 이용할 수 없는 상황이 발생함.&lt;/li&gt;
&lt;li&gt;이용할 수 있도록 컴포넌트의 테마를 주입하는 기능이 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Condition&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dark 테마를 지원해야 함.&lt;/li&gt;
&lt;li&gt;고객사에서 요청하는 프로젝트의 Dark테마&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Color se&lt;/b&gt;t과 추후 사내 플랫폼에서 사용할 Dark테마의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Color set&lt;/b&gt;이 다름&lt;/li&gt;
&lt;li&gt;단순히 정적인 light 테마, Dark 테마를 지원하는 것이 아닌 custom 테마를 생성하여 주입할 수 있는 기능이 필요함.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Develop Considerations&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React를 사용한 컴포넌트&lt;/li&gt;
&lt;li&gt;sass module을 사용하여 개발이 된 상태이며, &lt;b&gt;styled-component&lt;/b&gt; 또는 &lt;b&gt;emotionjs&lt;/b&gt;로 마이그레이션 할 시간적 여유가 없는 상황(정확히는 마이그레이션 할때 필요한 시간을 예측하기가 어려웠다.)&lt;/li&gt;
&lt;li&gt;각 컴포넌트 별 사용하는 색상이 정해져 있는 상태이며, 특정 컴포넌트만 그 색상을 바꿀 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 고려사항에 따라 Context API를 이용한 방법&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테마 상태를 관리하는 전역 상태 관리 시스템이 필요함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;React contextAPI&lt;/b&gt;를 이용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컴포넌트 별 사용할 색상 타입을 정의한다.&lt;/li&gt;
&lt;li&gt;UI 프로젝트에서 제공하는 Provider를 다른 프로젝트에 지원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Provider에 UI 프로젝트에 주입 될 color set을 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Provider에는 현재 선택된 theme와 theme의 color set을 파싱하여 scss에 넘겨준다.
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;document.documentElement.style.setProperty('--color', '#ffffff');&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;scss는 var 를 이용하여 색상을 받고 컴포넌트에 뿌려준다.&lt;/li&gt;
&lt;li&gt;UI 프로젝트에서 테마를 변경할 수 있는 hooks를 제공한다. - useTheme&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발생된 문제점&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 실행 시 깜빡임 현상이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useEffect에서 파싱 및 생성을 하여 scss에 주입하므로 최초에는 색상이 없는 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;default로 설정된 기본 테마가 아닌 커스텀 테마로 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위와 동일한 문제로 커스텀 테마로 띄울 경우 기본 테마로 설정된 색상이 보여진 후 커스텀 테마로 넘어갈 것이며 깜빡임 문제가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;고려한-해결-방법&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;고려한 해결 방법&lt;/h3&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Provider에서 완전히 계산이 완료 되기 전까지&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;display: none;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 설정한다.&lt;/li&gt;
&lt;li&gt;테마에 대한 계산 완료 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;display: block;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 변경하여 화면에 띄운다.&lt;/li&gt;
&lt;li&gt;문제점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 렌더링 시 테마를 계산한 후 화면에 출력하므로 계산할 테마 색상이 많아질수록 느려진다.&lt;/li&gt;
&lt;li&gt;transition이 걸려져 있는 경우 여전히 깜빡임 현상이 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;emotionjs를 도입한다.&lt;/li&gt;
&lt;li&gt;emotionjs를 도입하여 색상 부분만 emotionjs로 스타일을 주입한다.&lt;/li&gt;
&lt;li&gt;문제점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서 작성했듯 작업 소요시간이 예측이 되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;color: #212529; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 순위가 낮은 css 변수를 지원하며 css에서 직접 색상을 주입할 수 있도록한다.&lt;/li&gt;
&lt;li&gt;각 컴포넌트는 기본 제공 되는 css 변수를 사용하여 색상을 표현한다.&lt;/li&gt;
&lt;li&gt;Provider가 필요 없어지고 테마의 계산이 필요 없어진다.&lt;/li&gt;
&lt;li&gt;UI 프로젝트에서 제공하는 CSS를 사용하는 곳에서 우선순위가 높은 공간에서 색상을 재정의 한다.&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴포넌트 사용 시 러닝 커브가 추가적으로 발생한다.&lt;/li&gt;
&lt;li&gt;css 변수를 제공할 문서가 따로 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;solution&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Solution&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 해결 방법 중 &lt;b&gt;3번&lt;/b&gt;을 선택했다.&lt;/li&gt;
&lt;li&gt;sass module만을 쓰는 형태로, 렌더링 시 깜빡거림에 대한 이슈가 없다.&lt;/li&gt;
&lt;li&gt;컴포넌트에서 사용하는 css에 css 변수를 명시하고 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #212529; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;$background-color: var(--bg-color, #ffffff);
.component {
  background-color: $background-color;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위의 코드와 같이 사용한다면 프로젝트에서 --bg-color를 선언한 후 색상을 외부에서 자유롭게 변경할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1683460043654&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;:root {
  --background: #000000;
}

body {
  --background: #ffffff;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위와 같이 우선순위를 조절할 수 있다.&lt;/li&gt;
&lt;li&gt;또한 컴포넌트 상위에 변수를 덮어씌워 특정 컴포넌트만 색상을 변경할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 CSS에서 커스텀 테마를 적용할 수 있을 뿐만 아니라 특정 컴포넌트만 색상을 다르게 적용할 수 있는 등의 자유도 또한 부여할 수 있게 되었다.&lt;/p&gt;</description>
      <category>front-end/개발환경구축</category>
      <author>kmj24</author>
      <guid isPermaLink="true">https://kmj24.tistory.com/234</guid>
      <comments>https://kmj24.tistory.com/234#entry234comment</comments>
      <pubDate>Wed, 3 May 2023 21:47:14 +0900</pubDate>
    </item>
  </channel>
</rss>