반응형

 

React 애플리케이션을 개발할 때 성능 최적화는 중요한 핵심 요소입니다. 렌더링 성능을 제대로 최적화하지 않으면 불필요한 리렌더링으로 인해 앱의 전반적인 반응성이 크게 저하될 수 있습니다. 이번 글에서는 TypeScript 기반으로 useMemo, useCallback, React.memo를 활용한 성능 최적화 방법을 상세히 알아보겠습니다.

 

💂‍♀️ React 성능 최적화가 필요한 이유

React는 상태(state)나 props가 변경될 때마다 컴포넌트를 자동으로 다시 렌더링합니다. 그러나 모든 렌더링이 실제로 필요한 것은 아닙니다. 불필요한 렌더링이 과도하게 발생하면 애플리케이션의 성능이 현저하게 저하될 수 있습니다. 이러한 문제를 예방하고 개선하기 위해 메모이제이션(memoization) 기법을 효과적으로 활용할 수 있습니다. 

 

React에서 제공하는 최적화를 위한 hook 

1. useMemo → 값을 메모이제이션하여 불필요한 연산 방지
2. useCallback → 함수를 메모이제이션하여 불필요한 함수 재생성 방지
3. React.memo → 컴포넌트를 메모이제이션하여 불필요한 리렌더링 방지

 

하나씩 예제를 살펴보겠습니다. 

 

1. useMemo : State의 메모제이션

useMemo는 많이 소요되는 연산 결과를 저장하고, 의존성이 변경되지 않는다면 재계산하지 않도록 합니다.

즉, 불필요한 연산을 방지할 수 있습니다. 그러나 잘못 사용하게되면 오히려 오버헤드가 발생할 수도 있습니다.

import { useState, useMemo } from "react";

const ExpensiveComponent = () => {
  const [count, setCount] = useState<number>(0);
  const [text, setText] = useState<string>("");

  // 연산 비용이 큰 작업 (ex: 배열의 합 구하기)
  const expensiveValue = useMemo(() => {
    console.log("expensiveValue 계산 중...");
    return Array.from({ length: 10000 }, (_, i) => i).reduce((a, b) => a + b, 0);
  }, []); // 의존성 배열이 빈 배열이므로 한 번만 계산됨

  return (
    <div>
      <p>비싼 연산 결과: {expensiveValue}</p>
      <button onClick={() => setCount(count + 1)}>Count 증가: {count}</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
};

export default ExpensiveComponent;

2. useCallback: 함수의 메모제이션

useCallback은 함수의 재생성을 방지하여, 자식 컴포넌트에 전달할 때 불필요한 리렌더링을 막아줍니다.
자식 컴포넌트가 불필요하게 렌더링되지 않으나 확실히 의존성이 많아지면 코드가 점점 복잡해 질 수 있습니다. 

import { useState, useCallback } from "react";

const ChildComponent = ({ onClick }: { onClick: () => void }) => {
  console.log("ChildComponent 렌더링");
  return <button onClick={onClick}>클릭</button>;
};

const ParentComponent = () => {
  const [count, setCount] = useState<number>(0);

  // useCallback을 사용하여 함수 재생성을 방지
  const handleClick = useCallback(() => {
    console.log("버튼 클릭!");
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

export default ParentComponent;

3. React.memo : 컴포넌트의 메모제이션

React.memo는 props가 변경되지 않는 한 컴포넌트를 다시 렌더링하지 않도록 합니다.
불필요한 리렌더링을 방지할 수 있지만 useMemo와 useCallback과 함께 사용해야하는 경우가 생기면 코드가 점점 복잡해집니다. 

import React, { useState } from "react";

const MemoizedChild = React.memo(({ count }: { count: number }) => {
  console.log("MemoizedChild 렌더링");
  return <p>카운트 값: {count}</p>;
});

const ParentComponent = () => {
  const [count, setCount] = useState<number>(0);
  const [text, setText] = useState<string>("");

  return (
    <div>
      <MemoizedChild count={count} />
      <button onClick={() => setCount(count + 1)}>+1</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
};

export default ParentComponent;

 

 

최적화의 중요성은 잘 알고 있지만 어떠한 상확에 적절하게 적용하는건 생각보다 어렵습니다. 모든 곳에 사용하지 말고 필요한 곳에 적절히 사용하는것이 중요해 보입니다. 😏

반응형

'React' 카테고리의 다른 글

NextJS 에서 SEO 설정하기  (0) 2025.03.31
요즘 대세인 react 전역 상태관리 jotai 와 zustand  (0) 2025.03.28
커스텀 hook 만들기  (0) 2025.01.31
한글 초성 추출 하기  (7) 2023.10.10
React에서 clipboard에 copy&paste 하기  (0) 2023.10.09
반응형

SEO 를 설정하면 Google, Bing 과 같은 검색 엔진에 노출시켜 클롤링과 색인화 할 수 있습니다. 즉, 검색을 할때 잘 나타납니다. 주의할 점은 개발 중일때는 검색이 되면 안되니 주로 설정하지 않고 운영에만 설정해서 배포하도록 합니다. 또한 Facebook이나 Twitter, Kakaotalk 등 소셜 미디어에 링크를 공유할때도 어떤 페이지를 공유 했냐에 따라 썸네일 이미지나 제목등의 미리보기를 커스텀 할 수도 있습니다. 또한 검색 엔진에 의해 페이지의 성능과 접근성에 대한 적절한 판단을 받아 사용자 경험도 함께 향상되기 때문에 좋은 사이트가 됩니다. 😀

 

요즘은 다양한 라이브러리가 나와있기 때문에 손쉽게 설정이 가능합니다. 

다양한 방법이 있지만 크게 header에 직접 추가하거나 next-seo 라이브러리를 사용합니다. 

 

1. Header 에 직접 추가하기 

import Head from "next/head";

export default function Home() {
  return (
    <>
      <Head>
        <title>내 블로그 제목</title>
        <meta name="description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta name="keywords" content="Next.js, SEO, React, 웹개발" />
        <meta name="author" content="작성자 이름" />

        {/* Open Graph (Facebook, 카카오톡 등) */}
        <meta property="og:title" content="내 블로그 제목" />
        <meta property="og:description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta property="og:image" content="/og-image.png" />
        <meta property="og:url" content="https://myblog.com" />
        
        {/* Twitter Card (트위터 공유) */}
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content="내 블로그 제목" />
        <meta name="twitter:description" content="이 블로그는 SEO를 최적화한 Next.js 예제입니다." />
        <meta name="twitter:image" content="/twitter-image.png" />
      </Head>

      <h1>SEO 최적화된 블로그</h1>
      <p>이 페이지는 검색 엔진 최적화가 적용되었습니다.</p>
    </>
  );
}

 

📌 next/head 주의사항

  • title과 meta 태그는 페이지별로 맞춤 설정하는 것이 좋음.
  • 이미지(og:image, twitter:image 등)는 절대 경로로 설정해야 함.

2. next/seo 라이브러리 사용하기 

npm i next-seo

 

npm 으로 간단한게 설치가 가능합니다. 기전에 header 에 직접 추가하는 것보다 컴포넌트 단위로 수월하게 setting이 가능해서 실제 프로젝트에서 많이 사용합니다.

 

import { NextSeo } from "next-seo";

export default function Home() {
  return (
    <>
      <NextSeo
        title="내 블로그 제목"
        description="이 블로그는 SEO를 최적화한 Next.js 예제입니다."
        canonical="https://myblog.com"
        openGraph={{
          url: "https://myblog.com",
          title: "내 블로그 제목",
          description: "이 블로그는 SEO를 최적화한 Next.js 예제입니다.",
          images: [{ url: "https://myblog.com/og-image.png", width: 800, height: 600, alt: "Og Image Alt" }],
          site_name: "MyBlog",
        }}
        twitter={{
          handle: "@mytwitterhandle",
          site: "@site",
          cardType: "summary_large_image",
        }}
      />

      <h1>SEO 최적화된 블로그</h1>
      <p>이 페이지는 검색 엔진 최적화가 적용되었습니다.</p>
    </>
  );
}

 

하나의 컴포넌트에 다양한 옵션을 줄 수 있어서 meta  태그를 일일이 선언하지 않아서 편리합니다. defaultSeo 를 사용하면 모든 페이지에 공통 적용도 가능합니다. 

 

반응형
반응형

요즘 가장 대세처럼 많이 사용한다고 여기저기 사용한 코드를 인수인계 받거나 블로그 글도 많이 보았는데, 그래서 정확히 뭔지 궁금해짐. 둘 다 전역 상태관리 라이브러리 아닌가? 그래서 두 라이브러리가 어떻게 다를까? 

 

Jotai

jotai의 특징들 

- Atomic 한 상태관리 (Recoil 과 유사)

- atom() 을 사용한 개별적인 상태를 정의 할 수 있음

- 이렇게 개별화된 atom을 useAtom() 으로 사용 가능 

- 개별 atom단위로 구독

- atomWithAsync() 등등 으로 비동기 사용

- 상태가 독립적이거나 세분화된 경우에 사용하기 좋음

 

import { atom, useAtom } from 'jotai';

const countAtom = atom(0); // atom() 으로 상태 정의 

function Counter() {
  const [count, setCount] = useAtom(countAtom); // useAtom 으로 사용  

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button> // setCount() 를 통한 개별 업데이트 
    </div>
  );
}

export default Counter;

 

 

Zustand

- Store 기반한 상태관리 (Redux 와 유사)

- create() 를 사용한 상태 정의와 액션 정의

- useStore(), useZustand() 로 상태 업데이트 

- Store의 부분 상태를 구독 가능 

- set() 으로 비동기 로직 사용 가능

- 여러 상태를 한 번에 관리할 수 있음 

import { create } from 'zustand';

const useCounterStore = create((set) => ({ // store 정의 
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })), // set() 으로 상태를 변경 
}));

function Counter() {
  const { count, increase } = useCounterStore(); // 상태 구독

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increase}>+1</button>
    </div>
  );
}

export default Counter;

 

최종 결론

둘 중 하나만 써도 무방하겠지만 각각 필요한 역활에 조합해서 쓰는게 좋아보인다. 

데이터 관리는 zustand로 store에 저장하고, 테마 변경등의 독립적으로 상태를 구성하는 거는 jotai로 관리하는걸 추천한다고 한다. 

 

import { atom, useAtom } from 'jotai';
import { create } from 'zustand';

// Zustand Store 정의
const useUserStore = create((set) => ({
  user: { name: '홍길동', age: 25 },
  setUser: (user) => set({ user }),
}));

// Jotai 상태 정의
const themeAtom = atom('light');

function Profile() {
  const { user, setUser } = useUserStore();
  const [theme, setTheme] = useAtom(themeAtom);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
      <h2>{user.name}</h2>
      <button onClick={() => setUser({ name: 'Bob', age: 30 })}>Change Name</button>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>Toggle Theme</button>
    </div>
  );
}

export default Profile;

 

라떼는 redux 가 거의 유일무이였는데, 이제 다양한 관리 라이브러리도 나오는 걸 보니 계속 공부해야 겠음 

반응형

'React' 카테고리의 다른 글

React 성능 최적화: useMemo, useCallback, React.memo  (0) 2025.04.01
NextJS 에서 SEO 설정하기  (0) 2025.03.31
커스텀 hook 만들기  (0) 2025.01.31
한글 초성 추출 하기  (7) 2023.10.10
React에서 clipboard에 copy&paste 하기  (0) 2023.10.09
반응형

1. 빈값 체크 하기 (Null, Undefined)

form 작성할때 필수값 체크할때 무조건 필요해요. 단순히 value가 비어있을때 체크해도 되지만 확실하게 null값, undefined 값을 같이 비교해주면 완벽하게 비교할 수 있습니다.

function isEmpty(value) {
    return value === null || value === undefined || value === '';
}

// 사용 예시
console.log(isEmpty(null));  // true
console.log(isEmpty(''));    // true
console.log(isEmpty(0));     // false
console.log(isEmpty([]));    // false

 

 

2. 숫자인지 체크 

보통 input에 number 로 타입을 지정하면 대부분 체크가 되긴하지만 "e" 입력했을때 입력되는거 아시나요? ㅎㅎ 1e+6 이라고 입력했을때 수학적인 측면에서 1000000 을 뜻하기 때문에 e는 숫자체크를 해도 입력이됩니다. 이를 온전히 차단하고자 유효성 체크를 해주면 좋습니다.

function isNumeric(value) {
    return !isNaN(value) && typeof value === 'number';
}

// 사용 예시
console.log(isNumeric(123));    // true
console.log(isNumeric('123'));  // false
console.log(isNumeric(NaN));    // false
console.log(isNumeric(3.14));   // true

 

 

3. 이메일 타입 체크 

이 또한 input의 타입을 email로 체크해주면 웬만하면 체크해주긴 합니다만, 간혹 스크립트로 체크해야할 때가 있습니다. 

주로 정규식을 사용해서 체크합니다.

function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
}

// 사용 예시
console.log(isValidEmail('test@example.com'));  // true
console.log(isValidEmail('test@.com'));         // false
console.log(isValidEmail('test@example'));      // false

 

4. 전화번호 타입 체크 

한국에서 많이 사용하는 전화번호 체크입니다. 나라마다 번화번호 수가 다르니 한국한해서 사용 가능하지만, 진짜 많이 사용하는 유효성 체크 문법입니다. 역시 유효성 체크로 간단하게 할 수 있습니다.

function isValidPhoneNumber(phone) {
    const phoneRegex = /^01[016789]-\d{3,4}-\d{4}$/;
    return phoneRegex.test(phone);
}

// 사용 예시
console.log(isValidPhoneNumber('010-1234-5678'));  // true
console.log(isValidPhoneNumber('02-1234-5678'));   // false
console.log(isValidPhoneNumber('01012345678'));    // false

 

5. 한국 원화 타입 함수 

이건 유효성이라기 보다는 타입 변환이라고 보는게 더 맞겠네요. 그런데 이것도 진짜 많이 씁니다. 매번 귀찮게 찾아서 썼는데 이번기회에 저장해 놓고 걍 복붙해서 쓰려고요. 간단하게 만약 10000 일때 10,000 이렇게 표시해 주도록 해주는 겁니다.

function formatCurrencyKRW(amount) {
    let num = Number(amount); // "10000"일때 10000 로 변환
    if (isNaN(num)) { // 만약 "참새"같은 글자인 경우에는 return 
        return 'Invalid number';
    }
    return num.toLocaleString('ko-KR') + ' 원';
}

// 사용 예시
console.log(formatCurrencyKRW(1000000));    // "1,000,000 원"
console.log(formatCurrencyKRW('123456789'));// "123,456,789 원"
console.log(formatCurrencyKRW('10000'));    // "10,000 원"
console.log(formatCurrencyKRW('abc'));      // "Invalid number"
console.log(formatCurrencyKRW(NaN));        // "Invalid number"

 

 

아무리 AI 로 간단하게 검색해서 사용할 수 있다고 해도,

이렇게 자주 사용하는 유효성 체크를 정리해두면 바로 바로 복붙해서 사용하는게 훨씬 편하기 때문에 올려봅니다. 

반응형

'Javscript' 카테고리의 다른 글

Obejct 를 다루는 함수들  (0) 2023.09.13
문자열을 다루는 slice, split, substring  (0) 2023.09.09
배열을 다루는 forEach, map, filter  (0) 2023.09.08
반응형
import React from "react";
import useCounter from "./useCounter"; // 위에서 만든 useHook 

function CounterComponent() {
  const { count, increment, decrement, reset } = useCounter(5);
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>➕Increase</button>
      <button onClick={decrement}>➖Decrease</button>
      <button onClick={reset}>🔄Reset</button>
    </div>
  );
}

export default CounterComponent;

React에서 Custom Hook은 useState, useEffect, useRef 등의 기본 Hook을 조합하여 재사용 가능한 로직을 만들어 사용할 수 있게 해줍니다.
커스텀 훅의 네이밍 규칙은 "use"로 시작해야 하며, 내부에서 hook을 호출할 수 있습니다.

 

useHook의 기본구조를 살펴보면 아래와 같습니다.

import { useState, useEffect } from "react";

function useCustomHook() {
  const [state, setState] = useState(null);

  useEffect(() => {
    console.log("Custom Hook Mounted!");

    return () => {
      console.log("Custom Hook Unmounted!");
    };
  }, []);

  return [state, setState];
}

 

위의 구조를 참고해서 counter hook 을 만들고 호출도 해봅시다.

// 커스텀 useCounter 

import { useState } from "react";
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount((prev) => prev + 1);
  const decrement = () => setCount((prev) => prev - 1);
  const reset = () => setCount(initialValue);
  return { count, increment, decrement, reset };
}
export default useCounter;

 

이렇게 나만의 custom hook을 사용해서 재사용성을 높이고, 코드 분리를 하여 코드를 최척화 가능합니다. 

저는 모든 플젝에서 많이 사용하고 있습니다. 😉

반응형
반응형

퀴즈관련된 로직을 만들던 중 한글 초성 맞추기 관련 로직을 찾아보게되었습니다. 

흔히 쓰지 않는 로직이지만 이참에 정리해 두려고 합니다. 

 

한글의 초성을 추출하기 위해서는 각 한글 문자를 유니코드로 변환한 후, 해당 유니코드에서 특정 값을 빼서 초성의 유니코드를 얻어야 합니다. 그리고 그 유니코드를 다시 문자로 변환하여 초성을 얻을 수 있습니다.

 

const [extractInput, setExtractInput] = useState("");
const [extractChar, setExtractChar] = useState("");
const getInitials = (e) => {
    setExtractInput(e.target.value);
    const str = e.target.value;
    const cho = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
    let result = "";
    for (let i = 0; i < str.length; i++) {
        const charCode = str.charCodeAt(i);
        if (charCode >= 0xAC00 && charCode <= 0xD7A3) {
            const uni = charCode - 0xAC00;
            const choIdx = Math.floor(uni / (21 * 28));
            result += cho[choIdx];
        } else {
            result += str[i];
        }
    }
    setExtractChar(result);
}

return(
	<div>
        <input type="text" value={extractInput} onChange={getInitials} style={{border: "1px solid lightgrey"}} />
        <br/>
        <span>range value: {extractChar}</span>
    </div>
)

 

입력된 문자열의 각 문자에 대해 유니코드를 계산하여 초성을 추출하고, 그 결과를 state에 업데이트 해주면됩니다. 

 

charCode >= 0xAC00 && charCode <= 0xD7A3 이 부분은 한글 유니코드의 범위를 체크하는 조건입니다.

0xAC00은 '가'의 유니코드 값으로, 이는 한글 음절의 시작점이며, 초성이 'ㄱ', 중성이 'ㅏ', 종성이 없는 경우를 나타냅니다.
0xD7A3은 '힣'의 유니코드 값입니다. 이는 한글 음절의 끝점이며, 초성이 'ㅎ', 중성이 'ㅣ', 종성이 'ㅎ'인 경우를 나타냅니다.
즉, charCode >= 0xAC00 && charCode <= 0xD7A3 이 조건은 주어진 문자가 한글 음절인지를 확인합니다. 
이 조건이 true이면, 해당 문자는 '가'부터 '힣' 사이의 한글 음절이라는 것을 알 수 있습니다.

이 범위 내의 문자에 대해서만 초성을 추출하는 로직을 적용합니다. 만약 문자가 이 범위 밖에 있다면, 그 문자는 한글 음절이 아니므로 초성 추출 로직을 적용하지 않고, 원래 문자를 그대로 반환합니다.
만약 한글이 아닌 다른 글자가 들어가는경우 그대로 return 하게 됩니다.

반응형
반응형

어느날 문득 어떤 로컬에서 어떤 페이지를 열어도 이런 에러가 발생했다.

 

lockdown-run.js:17 Lockdown failed: TypeError: At intrinsics.Object.groupBy expected boolean not function

lockdown-more.js:99 Protecting intrinsics failed: ReferenceError: harden is not defined

 

이렇게 두가지 에러가 한번에 나왔다.

찾아보니 이건 chorme extension에서 발생되는 에러라고 한다. 

나같은 경우에는 Metamask 에서 오류가 발생됬다. 

또 무슨 npm버전관련된 에러인줄 알았는데, extension 에러라니... 🥹 다행임.

반응형
반응형

기존에는 document.execCommand('copy') 를 사용해서 구현을 했었는데, deprecated 되었다고 합니다. 

그래서 다른 방법을 찾아보다가 useRef 를 사요해서 navigator.clipboard.writeText() 함수를 사용했는데, 로컬에서는 잘 되다가 배포이후 안되서 찾아보니 보안상의 이유로 https 에서는 복사가 되지 않는다고 하네요. 

 

그래서 결국 라이브러리를 사용했습니다. "clipboard-copy"라는 라이브러리 입니다. 

먼저 설치를 해줍니다. 

npm install clipboard-copy

 

그리고 copy라는 함수를 import 해옵니다. 

이 copy라는 함수로 useRef를 사용하여 input의 value값을 클립보드에 복사하기를 할 껍니다. 

 

import copy from 'clipboard-copy';

const App = () => {
  const inputRef = useRef();
  const handleCopy = () => {
    copy(inputRef.current.value);
    alert('복사되었습니다!');
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="복사할 문구를 적으세요~" />
      <button onClick={handleCopy}>COPY!</button>
    </div>
  );
}

결과화면

반응형
반응형

대부분의 UI요소의 반응형은 css로도 처리가 가능합니다.

하지만 간혹 js 내에서 구조를 다시 그린다거나, className을 pc냐 모바일이냐에 따라 다시 그린다거나 하는 일이 자주 발생하기 때문에 js 내에서도 window의 사이즈를 실시간으로 알아야 하는 것이 필요로 합니다. 

 

React에서는 useEffect와 window의 size의 변화를 addEventListener함수를 통해 알 수 있습니다. 

 

import React, {useEffect, useState} from 'react'

const WindowWidth = () => {
  const [windowWidth, setWindowWidth] = useState(window.innerWidth)
  const handleResize = () => {
    setWindowWidth(window.innerWidth)
  }
  useEffect(() => {
    window.addEventListener('resize', handleResize)
    // 컴포넌트 언마운트 시 이벤트 리스너 삭제
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  return (
    <div>
      <p>Window Width: {windowWidth}px</p>
    </div>
  )
}

export default WindowWidth

 

1.  window.innerWidth : window의 width 값을 알 수 있습니다. 

2. return () => { window.removeEventListener('resize', handleResize) } : 컴포넌트가 DOM에서 제거될때 실행되는 클린업 함수입니다. 이걸 추가해 줘야 메모리 누수를 방지할 수 있습니다. 

 


이외 특정 width 값 이하일때 mobile로 구분하도록 해보겠습니다. 

저는 460을 기준으로 이하일때는 모바일 이상일 때는 pc라고 가정했습니다. 

 

import React, {useEffect, useState} from 'react'

const WindowWidth = () => {
  const [isMobile, setIsMobile] = useState(false)

  const handleResize = () => {
    window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)
  }
  useEffect(() => {
    window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)
    window.addEventListener('resize', handleResize)
    // 컴포넌트 언마운트 시 이벤트 리스너 삭제
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  return (
    <div>
      <p style={{color: `${isMobile ? "blue": "red"}`}}>
        {isMobile ? 'MOBILE' : 'PC'}
      </p>
    </div>
  )
}

export default WindowWidth

 

useEffect 내에 addEventListener로 연결하기 전

window.innerWidth < 461 ? setIsMobile(true) : setIsMobile(false)

이 부분을 한번 더 넣어주는 이유는 최초 실행 후 window의 resize가 일어날 때만 값이 바뀌기 때문에,

최초 화면이 mobile 화면이어도 초기 세팅값이 false이기 때문에 pc로 나옵니다. 

이렇게 resize가 일어나지 않았을 때 최초 렌더링 될 때 한번 체크를 해주기 위해 추가해 줬습니다. 

결과화면

은근히 자주 사용하는 window 실시간 width값 가져오기였습니다~

반응형
반응형

실제 프로젝트를 하다보면, 다양한 케이스가 존재합니다. 

우리가 보통 생각하는 map 은 데이터가 이미 존재하고 해당 데이터를 map 으로 돌린다. 

특히 리액트 프로젝트에서 return 부분에서는 for문을 사용할 수 없다.

라고 인식하기 때문에, 특정 숫자를 지정해서 반복문을 돌려야 하는 경우 불가능 하다고 생각하는 분들도 있을 것 같습니다. 

 

방법은 여러가지가 있습니다만, 만약 배열을 돌리는 수가 적은 경우 그냥 [1,2,3] 로 돌려도 됩니다. 

<div>
    {[1,2,3].map((_, idx) => (
      <p key={idx}>item {idx}</p>
    ))}
</div>

하지만, 돌려야하는 그 수가 많은경우 저렇게 사용하면 비효율적이기 때문에, Array함수를 사용하여 배열을 만들어 사용하는것이 좋습니다. 단, Array(5) 이렇게 하면 5자리 배열이 생성되는건 맞지만 각각의 요소는 undefined 이므로, 대신 fill로 대충 값을 채워준뒤 사용하면 됩니다. 

<div>
    {Array(5).fill(null).map((_, idx) => (
      <p key={idx}>item {idx}</p>
    ))}
</div>

결과화면

 

그러면 이를 이용해서 다음과 같은 배열도 만들수 있습니다. 

 

var menu=["햄버거", "치킨", "피자"]; 라는 데이터가 있는데,

무조건 5개의 UI가 나와야하고, 값이 없는경우는 "준비중"으로 표시한다고 가정해 봅시다.

그러면 우리는 위에서 배운 Array를 사용해서 구현할 수 있겠습니다. 

 

※ 아래 코드를 보기전에 문제처럼 먼저 구현해보면 좋을 것 같네요!😀

 

function App() {
  const menu = ["햄버거", "치킨", "피자"];
  return (
    <div>
      <ul>
        {
          menu.map((item) => (
            <li>{item}</li>
          ))
        }
        {
          Array(5 - menu.length).fill(0).map((item, idx) => (
            <li>준비중</li>
          ))
        }
      </ul>
    </div>
  )
}

 

결과화면

 

반응형

+ Recent posts