Post

[Day56] 쇼핑몰 실습 - 상품 상세

[Day56] 쇼핑몰 실습 - 상품 상세

_의 역할 – map에서 _는 뭘까?

1
2
3
4
5
skeletonArr.map((_, i) => (
    <li key={i}>
        <ProductCardSkeleton />
    </li>
))
  • map((_, i) => ...)에서 _는 사용하지 않을 첫 번째 인자(요소)를 의미
  • 보통 .map((item, index) => ...)처럼 쓰지만, item을 사용하지 않으면 _로 대체해서 의도를 명확히 표현할 수 있음

→ 즉, “나는 이 인자는 쓰지 않을 거야!” 라는 의미 !

❓ debounce vs throttle

  • 스크롤/입력/리사이즈 등 연속적으로 실행되는 이벤트를 조절해 성능 최적화
    • 💡 이벤트 발생을 제한해서 성능을 최적화하는 개념

✅ 디바운싱(Debouncing)

  • 연속적으로 발생하는 이벤트들 중 마지막 이벤트만 처리하는 기법
  • 일정 시간 내에 추가 이벤트가 발생하면 타이머를 재설정하고, 더 이상 이벤트가 발생하지 않을 때 최종적으로 함수를 실행

예시 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function debounce(func, delay) {
  let timeoutId;

  return function(...args) {
// 이전 타이머가 있으면 취소
    if (timeoutId) clearTimeout(timeoutId);

// 새로운 타이머 설정
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

// 사용 예시: 검색창 입력 디바운싱
const searchInput = document.getElementById('search');
const handleSearch = (e) => {
  console.log('Searching for:', e.target.value);
// API 호출 등의 작업
};

// 300ms 동안 추가 입력이 없을 때만 검색 수행
searchInput.addEventListener('input', debounce(handleSearch, 300));

✅ 스로틀링(Throttling)

  • 일정 시간 간격으로 함수를 최대 한 번만 실행하도록 제한하는 기법
  • 디바운싱과 달리 마지막 이벤트를 기다리지 않고, 정해진 주기마다 한 번씩 함수를 실행

예시 코드

1
2
3
4
5
6
7
8
9
10
export const throttle = (func, limit = 300) => {
  let inThrottle
  return function (...args) {
    if (!inThrottle) {
      func.apply(this, args)
      inThrottle = true
      setTimeout(() => (inThrottle = false), limit)
    }
  }
}

resize 이벤트가 연속적으로 발생해도 1초에 한 번만 실행

1
2
3
4
5
const handleResize = throttle(() => {
    if (window.innerWidth > 1100) {
      setIsOn(false)
    }
}, 1000)

→ 리사이즈 시 화면 너비가 1100보다 크면 isOn을 꺼주는데, 이걸 1초에 한 번만 실행하도록 조절

debounce와 throttle의 차이

개념디바운스쓰로틀
언제 실행?마지막 이벤트 후 한 번만 실행일정 시간마다 실행
예시 상황검색창 자동완성 (타이핑이 끝난 후 실행)스크롤 위치에 따라 UI 변경 (지속적으로 실행)

실제 프로젝트에서는 Lodash나 Underscore 같은 라이브러리의 구현체를 많이 사용


❓ React Router- loader의 역할

  • 페이지 렌더링 전에 데이터를 미리 받아오는 함수
  • useLoaderData()로 페이지에서 받아올 수 있음
  • useEffect보다 선제적으로 동작함 → 초기 로딩 시점 최적화
1
2
3
4
5
6
7
8
9
10
11
12
{
  path: '/detail/:productId',
  element: <DetailPage />,
  loader: async ({ params }) => {
    try {
      const product = await getProductById(params.productId)
      return product
    } catch (err) {
      console.log('err----', err)
    }
  },
}
  • DetailPage가 열리기 전에 productId에 해당하는 상품 데이터를 먼저 받아와서 넘겨줌.
  • 이렇게 하면 컴포넌트 안에서 useEffect로 따로 불러오는 게 아니라, 라우터에서 데이터를 미리 처리

❓ Vite 설정 – alias & proxy

🔹 alias

1
2
3
alias: {
  '@': path.resolve('./src'),
}
  • @src로 매핑해서 import 경로를 더 간결하게 쓸 수 있음
    • @/components/ComponentName 처럼 사용 가능

✅ proxy

개발 환경에서는 CORS(Cross-Origin Resource Sharing) 문제가 생길 수 있다.
브라우저 보안 정책에 따라,
예를 들어 프론트엔드가 localhost:5173에서 실행 중이고
API 서버가 localhost:3000일 경우,
직접 요청(5173 → 3000)은 보안상 차단된다.

이럴 때 Vite에서 proxy를 설정하면,
프론트엔드 서버를 통해 API 요청을 우회시킬 수 있다 !

브라우저는 이를 같은 출처(origin)에서의 요청으로 인식하게 되어,
CORS 오류 없이 API 요청이 정상적으로 작동한다.

1
2
3
4
5
6
7
8
9
server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, ''),
      },
    },
},
  • /api로 시작하는 모든 요청은 http://localhost:3000로 리다이렉트됨
  • changeOrigin: true는 호스트 헤더를 대상 URL의 호스트 이름으로 변경하여 CORS 문제를 방지
  • rewrite 함수는 URL에서 /api 접두어를 제거
    • /api/products를 요청하면 서버에는 http://localhost:3000/products로 전달

전체 Vite 설정 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    alias: {
      '@': path.resolve('./src'),
    },
  },
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, ''),
      },
    },
  },
})

❓ TypeScript – 경로 별칭 설정 (tsconfig.json)

1
2
3
4
5
6
7
8
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
    }
  }
}
  • TypeScript에서도 @ 경로로 import 가능!
  • import { Button } from '@/components/Button' 처럼 가독성도 좋아지고 유지보수도 쉬워짐.

💡 왜 tsconfig에도 alias 설정이 필요한가?

설정 대상역할
vite.config.js실행 중인 Vite 개발 서버용
tsconfig.jsonTypeScript 컴파일러와 VS Code가 경로 인식

둘 다 설정해야 import, 자동완성, 타입 검사 모두 완벽하게 작동!


상품 상세 페이지 (실습 + 과제)

day53


END

This post is licensed under CC BY 4.0 by the author.