Skip to content

Conversation

@grimza99
Copy link
Collaborator

요구사항

기본

####중고마켓

  • 중고마켓 페이지 주소는 “/items” 입니다.
  • 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.
  • 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.
  • 상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요.
  • '상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )
  • 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.

####중고마켓 반응형

  • 베스트 상품
  • Desktop : 4개 보이기
  • Tablet : 2개 보이기
  • Mobile : 1개 보이기
  • 전체 상품
  • Desktop : 12개 보이기
  • Tablet : 6개 보이기
  • Mobile : 4개 보이기

심화

  • 페이지 네이션 기능

주요 변경사항

  • 셀렉트 박스 react-select 라이브러리 사용

스크린샷

스크린샷 2025-01-21 오후 10 01 43 스크린샷 2025-01-21 오후 10 01 49

멘토에게

  • 이번에 react- select 라이브러리를 사용해봤는데, 너비가 제맘대로 잘 조정되지 않아 많이 힘들었습니다. 불러온 padding 값이 자동으로 설정되어있었는데, 모든 요소에 padding값을 지정해 보았지만 적용이 되지 않아서 참 아쉬웠습니다.
  • 어느 한부분의 문제가 전혀 다른 부분에 영향을 끼치는 경우가 많아서 당혹스러웠습니다... 렌더링이 자주 일어나면 스타일드 컴포넌트에서 영향을 끼치는 경우가 생기다니...

hanseulhee and others added 30 commits October 10, 2023 14:15
[Fix] delete merged branch github action
이미지 파일 이름 띄어쓰기제거,이름 변경

	-kdjkdjfkdjfdjk
컬러팔레트 사용을 위한 create color.css 작성
nerte and others added 22 commits December 27, 2024 15:54
login_signup.js 파일 작성, 핸들러함수를 계획하고, 각 함수가 쓰이는 html파일에 해당 js 파일을 연결, 그리고 해당 js 파일에 필요한 함수이름을 import 했다.
로그인 페이지의 비밀번호 부분 테두리 스타일과 8글자 이상 작성 에러메시지 , 빈값일 경우 에러메시지 기능 작성

중복되는 코드가 많아서 추후 정리 필요
기존에 border red 스타일이 간혹 사라졌다가 다시 나타나는 현상 수정함.
회원가입 버튼 활성화 부분 구현,
- 디렉토리 구조 정리
- 눈모양 i태그클릭시 type을 password, text로 수정하는 password_hide 함수작성,
- 에러메시지가 생김에 따라 i태그가 움직이는것 수정

-앞으로의 수정 : 코드의 중보정리, 모듈화 진행예정
중복되는 코드가 많아 signup_login.js 파일을 만들어 login.html, signup.html 두파일에 적용하려 했으나,
일부 기능이 잘 구현되지 않아 일단 test.js 파일을 작성하고 코드리뷰 이후 보완후에 util.js 파일로 지정하려 함.
Resolve conflicts in README.md and package.json
Merge branch 'React-유선향' of https://github.com/codeit-bootcamp-frontend/13-Sprint-Mission into React-유선향
화면 초기렌더링시 아이템 불러올수 있게 구현
베스트 아이템을 따로 리퀘스트하는 함수를 api.js에 생성,
ItemsList에 따로 prop내려줌.
pageSize, bestPageSize state로 실행 디바이스에 따라서 propducts 요청 쿼리스트링 변화
베스트 아이템과 전체 아이템 영역을 스타일링 다르게하고, 반응형으로 스타일이 달라지게 구현
@grimza99 grimza99 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jan 21, 2025
@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 23, 2025

스프리트 미션 하시느라 수고 많으셨어요.
학습에 도움 되실 수 있게 꼼꼼히 리뷰 하도록 해보겠습니다. 😊

@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 23, 2025

이번에 react- select 라이브러리를 사용해봤는데, 너비가 제맘대로 잘 조정되지 않아 많이 힘들었습니다. 불러온 padding 값이 자동으로 설정되어있었는데, 모든 요소에 padding값을 지정해 보았지만 적용이 되지 않아서 참 아쉬웠습니다.

아핳 select 라이브러리를 경험해보셨군요. 기능이 많이 들어가있는 라이브러리는 커스텀하기 어려울 수 있지요 😢

어느 한부분의 문제가 전혀 다른 부분에 영향을 끼치는 경우가 많아서 당혹스러웠습니다... 렌더링이 자주 일어나면 스타일드 컴포넌트에서 영향을 끼치는 경우가 생기다니...

그치요 ㅠㅠ 사이드 이펙트라고도 합니다. 그럴 경우 디버깅하기 정말 힘들죠 ㅠㅠ

Comment on lines +3 to +23
const useWindowSize = () => {
const [deviceType, setDeviceType] = useState("desktop");
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};

useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return deviceType;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

크으 👍👍👍👍 커스텀 훅으로 만드셨군요 !

해당 기능은 여러 페이지와 여러 컴포넌트에서 필요한 페이지이지요 ! 굿굿 ! 좋은 아이디어예요 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!

Comment on lines +5 to +21
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};

useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleResizeuseEffect 내부에 선언하는게 어떨까요?

Suggested change
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};
useEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
useEffect(() => {
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);

useEffect 내부에 함수를 선언하면, 해당 함수가 useEffect의 클로저 컨텍스트 안에 들어가기 때문에, 최신 상태와 데이터를 참조할 수 있으며, 리렌더링에 의한 불필요한 리렌더링을 방지할 수 있습니다 !
또한, 서로 필요한 로직이 가까이 있어 가독성도 향상될 수 있어요 😊

Comment on lines +5 to +13
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

디바운싱/쓰로틀링을 통하여 성능 최적화를 시켜볼까요?

Suggested change
const handleResize = () => {
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};
const handleResize = () => {
console.log("Hello !", Date.now()); // 👈👈
if (window.matchMedia("(max-width: 767px)").matches) {
setDeviceType("mobile");
} else if (window.matchMedia("(max-width: 1199px)").matches) {
setDeviceType("tablet");
} else {
setDeviceType("desktop");
}
};

해당 이벤트에 console.log를 호출해보면 리사이징이 될 때마다 정말정말 많은 호출하는 것을 볼 수 있을거예요 !
그만큼 성능에 좋지 못하다는 이야기겠지요?
따라서, 프론트엔드 개발자들은 이렇게 잦은 이벤트가 발생할 때(리사이징, 스크롤, 타이핑 등) 디바운싱/쓰로틀링을 통하여 최적화를 시키곤 합니다.

쓰로틀링(Throttling): 일정 시간 동안 하나의 함수만 호출되도록 하는 기법입니다. 예를 들어, 사용자가 스크롤을 할 때, 매번 이벤트를 처리하지 않고 일정 간격으로 한 번만 처리하게 합니다. 이를 통해 성능을 향상시킬 수 있습니다.

디바운싱(Debouncing): 여러 번 발생하는 이벤트 중 마지막 이벤트가 발생한 후 일정 시간이 지난 다음에 한 번만 실행되도록 하는 기법입니다. 예를 들어, 사용자가 검색어를 입력할 때, 입력이 끝난 후 일정 시간 동안 추가 입력이 없으면 검색 요청을 보냅니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예를 들어서 다음과 같은 코드가 있습니다:

function Container() {
    useEffect(() => {
      window.addEventListener('resize', handleResize);
      return () => {
        window.removeEventListener('resize', handleResize);
      }, []);
// ... Some code
}

디바운싱으로 다음과 같이 최적화를 시킬 수 있습니다 !:

function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

function Container() {
  const handleResize = () => {
    console.log('Window resized');
  };

  useEffect(() => {
    const debouncedHandleResize = debounce(handleResize, 300);
    window.addEventListener('resize', debouncedHandleResize);
    return () => {
      window.removeEventListener('resize', debouncedHandleResize);
    };
  }, []);

  // ... Some code
}

이렇게 하면 연속된 이벤트가 끝난 후 0.3초마다 호출하게 되어 기존보다 훨씬 최적화된 기능이 될 수 있습니다 !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

같은 함수를 매번 작성하기 번거롭지요. 선향님께서 하신 것처럼 이것도 훅으로 만들 수 있습니다 !

번거롭죠.. (사실 저도 위 코드는 GPT한테 맡긴 코드입니다 하하하핳ㅎ 그래서 될지는 장담못함..)
디바운싱, 쓰로틀링은 정말 흔한 기법이어서 참조할 수 있는 문서가 많습니다 !

useHooks

해당 라이브러리를 사용한 코드로 볼까요?

import { useRef, useState } from 'react';
import { useDebounceCallback } from 'usehooks-ts';

function useDebounceValue(initialValue, delay, options) {
  const eq = options?.equalityFn ?? ((left, right) => left === right);
  const unwrappedInitialValue = typeof initialValue === 'function' ? initialValue() : initialValue;
  const [debouncedValue, setDebouncedValue] = useState(unwrappedInitialValue);
  const previousValueRef = useRef(unwrappedInitialValue);

  const updateDebouncedValue = useDebounceCallback(setDebouncedValue, delay, options);

  // Update the debounced value if the initial value changes
  if (!eq(previousValueRef.current, unwrappedInitialValue)) {
    updateDebouncedValue(unwrappedInitialValue);
    previousValueRef.current = unwrappedInitialValue;
  }

  return [debouncedValue, updateDebouncedValue];
}

export default useDebounceValue;

그리고 사용 방법:

import React, { useState } from 'react';
import useDebounceValue from './useDebounceValue';

const ExampleComponent = () => {
  const [inputValue, setInputValue] = useState('');
  const [debouncedValue] = useDebounceValue(inputValue, 500);

  const handleChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleChange} placeholder="Type something..." />
      <p>Debounced Value: {debouncedValue}</p>
    </div>
  );
};

export default ExampleComponent;

}) {
const order = selectedOrder === "최신순" ? "recent" : "favorite";
const pageSize = device === "mobile" ? 4 : device === "tablet" ? 6 : 10;
const query = `?orderBy=${order}&page=${page}&pageSize=${pageSize}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

쿼리는 URLSearchParams로 손쉽게 사용할 수 있어요 !

(new URLSearchParams({hello: 1})).toString();
// 결과값: 'hello=1'

객체로 구성할 수 있어 가독성이 좋고, URL 인코딩을 자동으로 처리하여 특수 문자나 공백이 포함된 값에서도 안전하게 동작합니다 !

URLSearchParams: URLSearchParams 인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.

@@ -0,0 +1,75 @@
import Select from "react-select";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀주신 react-select 군요 !

라이브러리를 경험하셨군요 ! 좋습니다 ! 👍
다음에는 드롭다운을 직접 만들어보는 것도 좋은 학습 경험이 될 수 있을 것 같아요. 😊

@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 23, 2025

수고하셨습니다 선향님 !
나날이 빠르게 학습하시는게 느껴지네요 ㅎㅎㅎ
특히 커스텀 hook을 만드신 부분이 인상깊습니다 👍👍👍
학습 진도를 잘 따라가고 계셔서 조금 응용과 팁 위주로 리뷰하였습니다 ! 시간 되실 때 디바운싱도 한 번 적용해보세요 😊

스프린트 미션 수행하시느라 정말 정말 수고 많으셨습니다 !
이번 기초프로젝트에서 선향님의 역량은 팀에 크게 기여 되실거라고 확신합니다. 🤩

@kiJu2 kiJu2 merged commit 8dfe0fb into codeit-bootcamp-frontend:React-유선향 Jan 23, 2025
@grimza99
Copy link
Collaborator Author

감사합니다! 이번 팀프로젝트때 다른 팀원분들 경험을 저도 많이 흡수 할수 있는 좋은기회인것 같아서 희진님을 열심히 괴롭히고 있습니다!

@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 23, 2025

감사합니다! 이번 팀프로젝트때 다른 팀원분들 경험을 저도 많이 흡수 할수 있는 좋은기회인것 같아서 희진님을 열심히 괴롭히고 있습니다!

ㅋㅋㅋㅋ 열심히 괴롭혀서 많이 빼먹으세요 😊👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants