💡 URECA/🗒️ 스터디 노트

[URECA] Day55 React Shop PROJ 4편 디바운스 & 쓰로틀 정복기, 프록시 서버 설정

코딩하세현 2025. 4. 17. 11:40
728x90

📃 features.js

export const formatCurrency = number => {
  return number.toLocaleString() + '원'
}

export const formatDate = date => {
  const d = new Date(date)
  const year = d.getFullYear()
  // getMonth()는 0부터 시작하므로 1을 더하고, 10보다 작으면 앞에 0 추가
  const month = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')

  return `${year}. ${month}. ${day}`
}

// 디바운스 : 연속된 호출을 지연시켜 한번만 실행. 함수(함수, 대시시간)
export const debounce = (func, delay = 300) => {
  let timerId
  return function (...args) {
    if (timerId) clearTimeout(timerId)
    timerId = setTimeout(() => {
      func.apply(this, args)
    }, delay)
  }
}

// 쓰로틀 : 일정 시간 동안 한 번만 실행. 함수(함수, 대시시간)
export const throttle = (func, limit = 300) => {
  let inThrottle
  return function (...args) {
    // 일반 함수로 변경
    if (!inThrottle) {
      func.apply(this, args)
      inThrottle = true
      setTimeout(() => (inThrottle = false), limit)
    }
  }
}

 

🔍 하나하나씩 알아보자!

export const formatDate = date => {
  const d = new Date(date)
  const year = d.getFullYear()
  // getMonth()는 0부터 시작하므로 1을 더하고, 10보다 작으면 앞에 0 추가
  const month = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')

  return `${year}. ${month}. ${day}`
}

 

🤔 .padStart가 뭐지?

str.padStart(targetLength [, padString])
  • targetLength: 최종 문자열의 길이
  • padString: 앞을 채울 문자(기본은 ' ' 공백)

d.getMonth() + 1을 했다. 0월이 없으므로 +1을 해 1월을 의미하고

padStart를 통해 2자리의 길이를 의미하며 한자리 숫자일 경우 01월, 02월을 의미한다.

const day도 마찬가지이다.

return문을 통해서 년, 월, 일을 나타낸다. 


🤔 여기서의 func는 무엇을 의미하는 걸까?

// 디바운스 : 연속된 호출을 지연시켜 한번만 실행. 함수(함수, 대시시간)
export const debounce = (func, delay = 300) => {
  let timerId
  return function (...args) {
    if (timerId) clearTimeout(timerId) // 이전 시간을 지우고
    timerId = setTimeout(() => {
      func.apply(this, args) // 일정 시간 후 func실행
    }, delay)
  }
}
// => func.apply(this, apply)는 원래 전달된 함수를 원래의 this와 인자로 실행시켜주는 함수이다.

debounce 함수에 인자로 전달되는 실제로 실행하고 싶은 함수를 의미한다. 

func는 "내가 나중에 하고 싶은 행동"을 의미한다.

그렇다면 debounce는 그 행동을 일정 시간동안 지연시키는 함수를 만드는 역할을 한다. 

 

더보기
const handleInput = (e) => {
  console.log('입력 중:', e.target.value)
}

const debouncedInput = debounce(handleInput, 500)

inputElement.addEventListener('input', debouncedInput)

handleInput이 바로 func로 전달이 된다. 

debouncedInput은 handle을 0.5초로 지연시키는 함수이므로

사용자가 입력할 때마다 즉시 실행하는 것이 아니라!

입력이 멈춘 후 0,5초 뒤에 실행된다. 


📘  그렇다면! 디바운스란 무엇인가?

디바운싱은 연속적으로 발생한 이벤트들 중 마지막 이벤트만 처리하는 기법이다.

일정 시간내 추가 이벤트가발생하면 타이머를 재설정하고, 더 이상 이벤트가 발생하지 않을 때 최종적으로 함수 실행한다.

 

📘 그렇다면 위에 적힌 스로트링은 무엇인가?

일정 시간 간격으로 함수를 최대 한번 만 실행하도록 제한하는 기법

이벤트를 기다리지않고, 정해진 주기마다 한번씩 함수를 실행한다. 

// 쓰로틀 : 일정 시간 동안 한 번만 실행. 함수(함수, 대시시간)
export const throttle = (func, limit = 300) => {
  // 지금 실행 여부를 판단하는 inThrottle
  let inThrottle
  //   throttle된 새로운 함수를 반환 (원본은 실행 안 함)
  return function (...args) {
    // 일반 함수로 변경
    if (!inThrottle) {
      // 원래 함수 실행
      func.apply(this, args)
      inThrottle = true
      setTimeout(() => (inThrottle = false), limit)
    }
  }
}
// => inTrottle이 false일때 한정되는것, limit의 시간이 지날 때까지 다시 실행되지 않도록 막는 함수

👉🏻 Header 컴포넌트에 throttle을 사용하는 이유와 동작 원리

import React, { useEffect, useState } from 'react'
...
import { throttle } from '@/utils/features'

const Header = () => {
...

  const [isOn, setIsOn] = useState(false)
  const location = useLocation()

  const addClassOn = () => {
    setIsOn(!isOn)
  }

  useEffect(() => {
    setIsOn(false)
  }, [location.pathname])

  // 2. throttle된 handleResize 동작
  // window.resize이벤트가 다량으로 발생해도 hanldResize는 1초에 한번만 실행
  const handleResize = throttle(() => {
    if (window.innerWidth > 1100) {
      setIsOn(false)
    }
  }, 1000)

  // 1. 컴포넌트 마운트
  // useEffect는 컴포넌트가 처음나타날때 한 번 실행
  // window에 resize이벤트가 나타날땨ㅐ마다 hanleResize를 호출한다.
  useEffect(() => {
    window.addEventListener('resize', handleResize)
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, [handleResize])
  
// [handleResize]를 넣는 이유 = cleanup할 때 정확히 같은 함수를 지우기 위해


...

export default Header

 

정리
throttle은 resize 같은 과도한 이벤트 호출을 제한해주는 친구!
1초에 한 번만 함수 실행되도록 제한함.
Header.jsx에서는 창 크기가 1100px 이상이면 메뉴를 닫는 용도!
성능 최적화 + 사용자 경험 개선을 동시에!

📘 프록시 서버 설정

프록시 서버란? 클라이언트와 서버 사이에 위치한다. 

vite의 서버 프록시 설정은 개발 중에 발생하는 CORS(Cross-Origin-Resouce Sharing)문제를 해결하고 API 요청을 더 효율적으로 관리하기 위해 사용된다.

 

1. CORS문제 해결

2. API 경로 단순화

3. 환경 간 일관성 

 

📖 프록시 서버 vs vpn 차이

프록시 서버와 vpn은 모두 목적지로 가는 도중에 서버를 통해 트래픽을 라우팅한다. 이를 통해 트래픽 필터링, 정책 시행 및 이와 유사한 이점을 얻는다. 

vpn은 항상 사용자와 클라이언트 간의 트래픽을 암호화하며, 클라이언트의 트래픽은 단순히 목적지만 전달한다. 

반면 프록시 서버는 서버로 이동하는 트래픽을 암호화하지 않을 수 있으며 클라이언트에 익명성을 제공한다. 

(1) New Messages!

 

프록시 서버란 무엇입니까? - 체크 포인트 소프트웨어

프록시 서버가 무엇인지, 프록시 서버를 통해 트래픽에 악성 콘텐츠가 있는지 검사하고 조직에서 기업 보안을 적용하는 방법에 대해 알아봅니다.

www.checkpoint.com


🐛 실습 중 겪은 문제

코드를 잘 따라 쳤는데 notFound가 뜨는 오류를 발생했다. 

아뿔싸.. 단 하나의 오타로 인해 삽질을 했다. 

formmatCourrently에서 오타가 발생한 것이다. m을 두번쓰는..

자습시간에 잘 고쳐서 화면 렌더링이 잘되는것을 확인할 수 있었다. 

 

더보기

어제본 개념은 오늘다시 봐도 새롭다

아 도라에몽의 암기빵이 필요하다.

728x90