Project_WePet_회고_Tech🐾

YeonnΒ·2023λ…„ 6μ›” 11일
0

Project_WePet

λͺ©λ‘ 보기
3/3
post-thumbnail

ν”„λ‘œμ νŠΈ ν‚€λ…ΈνŠΈ 🌼

https://www.canva.com/design/DAFliNTrCd4/RHXx6U4dx9AxCbEiw04JCA/edit?utm_content=DAFliNTrCd4&utm_campaign=designshare&utm_medium=link2&utm_source=sharebutton

1. Nav animation

.line {
    z-index: 1000;
    width: 30px;
    height: 3px;
    margin: 7px 0px;
    transition-duration: 0.5s;
    background-color: #000;
  }
  
  .hamTopLine {
    transform: translateY(10px) rotate(45deg);
    transform-origin: center;
  }
  
  .hamMidLine {
    opacity: 0;
  }
  
  .hamBtmLine {
    transform: translateY(-10px) rotate(-45deg);
    transform-origin: center;
  }
}

ham λ²„νŠΌμ΄ λ‹¨μˆœνžˆ μœ„μΉ˜ν•΄ μžˆλŠ” 게 μ•„λ‹ˆλΌ μœ μ €μ˜ μ•‘μ…˜μ— λ°˜μ‘ν•˜μ—¬ 움직이도둝 ν•˜κ³  μ‹Άμ—ˆλ‹€ !
3개의 라인 쀑 κ°€μš΄λ° 라인은 opacityλ₯Ό μ£Όμ–΄ μ•ˆλ³΄μ΄κ²Œ μ²˜λ¦¬ν•˜κ³  μ΅œμƒλ‹¨, μ΅œν•˜λ‹¨μ˜ 라인은 transform-origin: center λ₯Ό μ£Όμ–΄ κ°€μš΄λ°λ₯Ό 기점으둜 μ›€μ§μ΄κ²Œ ν•΄μ„œ Xλ²„νŠΌμ„ λ§Œλ“€λ„λ‘ ν–ˆλ‹€.

2. Nav @keyframes

animation: modalShowRight 0.3s;
@keyframes modalShowRight {
  from {
    opacity: 0;
    margin-right: -50px;
  }
  
  to {
    opacity: 1;
    margin-right: 0;
  }
}

animation 속성을 μ‚¬μš©ν•˜μ—¬ modalShowRight μ• λ‹ˆλ©”μ΄μ…˜μ˜ μ§€μ†μ‹œκ°„μ„ 0.3초둜 μ§€μ •ν•΄ λΆ€λ“œλŸ¬μš΄ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ—°μΆœν•˜κ³ 
keyframe ( fromκ³Ό to )λ₯Ό μ‚¬μš©ν•˜μ—¬ 였λ₯Έμͺ½μ—μ„œ μ™Όμͺ½μœΌλ‘œ λ‚˜μ˜€λŠ” ν˜•νƒœμ˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ—°μΆœν–ˆλ‹€.

βœ… μ• λ‹ˆλ©”μ΄μ…˜ 없이 λͺ¨λ‹¬μ°½μ΄ κ·Έλƒ₯ 떴을 λ•Œμ™€ μ• λ‹ˆλ©”μ΄μ…˜μ„ λΆ€μ—¬ν•΄μ„œ λΆ€λ“œλŸ½κ²Œ λͺ¨λ‹¬μ°½μ΄ 떴을 λ•Œ 완성도 차이가 많이 λ‚˜ λ³΄μ˜€λ‹€ ! κ΄€λ ¨ cssλ₯Ό μ’€ 더 곡뢀해봐야 ν•  것 κ°™λ‹€.

3. let token = localStorage.getItem('TOKEN')

localStorage에 TOKEN의 유무λ₯Ό ν™•μΈν•˜μ—¬ useNavigate hook으둜 결과에 λ§žλŠ” νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•œλ‹€.

4. config.js

export const BASE_URL = 'http://10.58.52.229:3000'

export const APIS = {
  product: `${BASE_URL}/products`,
}

useEffect(() => {
    fetch(`${APIS.product}/details/${productId}`)
      .then(response => response.json())
      .then(result => {
        console.log(result)
        setProductData(result.data)
      })
  }, [])

API μ£Όμ†Œκ°€ 자주 λ³€κ²½λ˜λ‹€ λ³΄λ‹ˆ κ·Έ λ•Œλ§ˆλ‹€ 각 νŽ˜μ΄μ§€μ˜ fetchλ₯Ό μ°Ύμ•„ μ‚­μ œν•˜λ €λ‹ˆ λ„ˆλ¬΄ λ²ˆκ±°λ‘œμ› λ‹€.
config.jsνŒŒμΌμ„ src 폴더 내에 λ§Œλ“€μ–΄μ„œ URL 을 μ„€μ •ν•˜κ³  각 μ—”λ“œν¬μΈνŠΈλ§ˆλ‹€ 미리 μž‘μ„±ν•΄λ‘μ—ˆκ³ ,
이후 변동이 생길 μ‹œ config.js 파일 λ‚΄μ˜ URL만 μˆ˜μ •ν•˜λ©΄ λ˜λ„λ‘ ν–ˆλ‹€.

5. if (!productData?. productPrice ) return null

useEffect λ‚΄λΆ€μ˜ 데이터λ₯Ό 뢈러였기 전에
UI λ‚΄λΆ€μ˜ map μ΄λ‚˜ reduce 같은 λ©”μ„œλ“œλ₯Ό λ§Œλ‚˜λ©΄ λ Œλ”λ§μ΄ μ€‘μ§€λ˜κ³  μ—λŸ¬λ©”μ„Έμ§€κ°€ λ–΄λ‹€.

useEffect둜 데이터 λΆˆλŸ¬μ˜€κΈ°κ°€ λ Œλ”λ§ λ’€λ‘œ λ°€λ €λ‚˜μ„œ
아직 mapμ΄λ‚˜ redudceλ₯Ό μ‹€ν–‰ν•  배열이 λΆˆλŸ¬μ™€μ§€μ§€ μ•Šμ•„μ„œμ˜€λŠ”λ°,

μ²˜μŒμ—λŠ”

if (!productData) return null

만 μž‘μ„±ν–ˆμ—ˆλ‹€. κ·Έλž¬λ”λ‹ˆ μ–΄λ–€ 뢀뢄은 해결이 되고 해결이 λ˜μ§€ μ•Šκ³  μ—λŸ¬λ©”μ„Έμ§€λ₯Ό λ„μš°λŠ” 일뢀가 λ‚¨μ•˜λ‹€.
κ·Έλž˜μ„œ ?. (μ˜΅μ…”λ„ 체이닝 μ—°μ‚°μž) λ₯Ό ν™œμš©ν•΄ productData 객체와 productPrice ν”„λ‘œνΌν‹°μ˜
쑴재 μ—¬λΆ€λ₯Ό ν•œλ²ˆ 더 μ²΄ν¬ν•˜λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ„œ ν•΄κ²°ν–ˆλ‹€.

  • ?. μ˜΅μ…”λ„ 체이닝 μ—°μ‚°μž
    ?. μ•žμ˜ 평가 λŒ€μƒμ΄ undefinedλ‚˜ null이면 평가λ₯Ό λ©ˆμΆ”κ³  undefinedλ₯Ό λ°˜ν™˜ν•œλ‹€.

6. toLocaleString( )

product detail νŽ˜μ΄μ§€μ™€ purchase νŽ˜μ΄μ§€μ—μ„œ μƒν’ˆ κ°€κ²©μ΄λ‚˜ 총계 등을 λͺ…μ‹œν•  λ•Œ
number type으둜 계산을 ν•˜κ³  계산을 마친 λ’€ toLocaleString() 둜 μ‚¬μš© 언어에 λ§žλŠ” ν‘œν˜„μ„ ν¬ν•¨ν•΄μ£Όμ—ˆλ‹€.

  • number에 μ‚¬μš© μ‹œ 1,000 처럼 ν•„μš”ν•œ 곳에 μ‰Όν‘œλ₯Ό 찍어쀀닀.
    date에 μ‚¬μš© μ‹œ λ¬Έν™”κΆŒμ— λ§žλŠ” μ‹œκ°„ν‘œκΈ°λ²•μœΌλ‘œ λ…„, μ›”, 일, μ‹œκ°„μ„ 리턴해쀀닀.
    array에 μ‚¬μš©μ‹œ array의 λ‚΄μš©μ„ λͺ¨λ‘ 문자둜 λ°˜ν™˜ν•΄μ„œ 보내쀀닀.

7. window.scrollTo(xμ’Œν‘œ, yμ’Œν‘œ)

 onClick={() => {
            const element = document.querySelector('.detailInformationBox')
            if (element) {
              const y = element.getBoundingClientRect().top - 100
              window.scrollTo({ top: y, behavior: 'smooth' })
            }
          }}

μƒν’ˆ μ •λ³΄λ‚˜ 리뷰λ₯Ό ν΄λ¦­ν–ˆμ„ λ•Œ μ›ν•˜λŠ” μœ„μΉ˜λ‘œ 슀크둀이 μ΄λ™ν•˜λ„λ‘ κ΅¬ν˜„ν•˜κ³  μ‹Άμ—ˆλ‹€ !
window. scrollTo() 둜만 μž‘μ„±ν•˜λ©΄ κ³ μ •λœ y값을 μ§€μ •ν•˜κΈ° λ•Œλ¬Έμ—
컨텐츠 양에 변동이 μžˆμ„ λ•Œ λ§ˆλ‹€ μƒˆλ‘œ μ§€μ •ν•΄μ€˜μ•Ό ν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ μžˆμ—ˆλ‹€.
κ·Έλž˜μ„œ νŠΉμ • μš”μ†Œμ˜ 값을 μ§€μ •ν•˜κΈ° μœ„ν•΄ querySelector둜 νŠΉμ • ν΄λž˜μŠ€μ— ν•΄λ‹Ήν•˜λŠ” μš”μ†Œλ₯Ό μ°Ύμ•„ element에 λ‹΄μ•„ μ„ μ–Έν•˜κ³ 
element.getBoundingClientRect().top 을 μ΄μš©ν•΄ querySelector둜 찾은 μš”μ†Œμ˜ 상단 경계가
뷰포트의 상단과 μ–Όλ§ˆλ‚˜ λ–¨μ–΄μ§ˆ 것인지λ₯Ό μ§€μ •ν–ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ window.scrollTo()에 μœ„μ—μ„œ μ •ν•œ 값을 top에 μ§€μ •ν•˜κ³ 
behavior: 'smooth'λ₯Ό 톡해 슀크둀이 λΆ€λ“œλŸ½κ²Œ μ΄λ™ν•˜λ„λ‘ ν–ˆλ‹€.

      <div className={`imgBox ${isCarousel ? '' : 'carousel'}`}>
        <img
          className="mainImg"
          src={productData.mainThumbnailImage}
          alt="productImage"
        />
        <img
          className="mainImg"
          src={productData.mainThumbnailImage}
          alt="productImage"
        />
      </div>

//scss
  .imgBox {
    display: flex;

    transform: translate(0px);
    transition: 1s;
    img {
      width: 100%;
    }
  }

  .carousel {
    transform: translate(-500px);
  }

.mainImg {
  width: 500px;
  flex: none;
  object-fit: contain;
}

λ¨Όμ € imgBox div μ•ˆμ— carousel κΈ°λŠ₯으둜 λŒμ•„κ°€κ²Œ ν•  이미지λ₯Ό 2개 λ‚˜μ—΄ν–ˆλ‹€.
mockdata둜 μž‘μ—…ν•  λ•ŒλŠ” λ°°μ—΄λ‘œ κ΄€λ¦¬ν•΄μ„œ index둜 2개의 이미지λ₯Ό μž‘μ—…ν–ˆμ—ˆλŠ”λ°,
λ°±μ—”λ“œμ™€ 톡신을 ν•˜κ²Œ λ˜λ©΄μ„œ μˆ˜μ •λ˜μ—ˆλ‹€.

κΈ°λ³Έ imgBox 에 transform : translate(0px);을 μ£Όμ–΄ 기본값을 μ£Όκ³  λ²„νŠΌμ„ λˆŒλ €μ„ λ•Œ
state 변경에 λ”°λ₯Έ carouselμ΄λΌλŠ” className을 μΆ”κ°€ν•˜μ—¬ translateλ₯Ό μƒˆλ‘œ λΆ€μ—¬ν•΄ xμΆ•μœΌλ‘œ λ°€μ–΄μ£Όμ—ˆλ‹€.
κΈ°λ³Έ class style에 transition을 μ£Όμ–΄ λΆ€λ“œλŸ½κ²Œ 이미지가 λ„˜μ–΄κ°ˆ 수 μžˆλ„λ‘ ν–ˆλ‹€.

사싀 μ²˜μŒμ—λŠ” 이미지 λ‚˜μ—΄μ„ λ°°μ—΄λ‘œ 관리해보고 μ‹Άμ—ˆλŠ”λ°, λ‹€μŒμ—λŠ” μ’€ 더 λ°°μ—΄λ‘œ κ΄€λ¦¬ν•˜λŠ” 방법을 찾아보고 μ‹Άλ‹€.

9. TOKEN을 뢀여받은 νšŒμ›μ˜ 배솑지 정보 등둝(POST), input value 관리

// purchase page
const [inputValue, setInputValue] = useState({
    name: '',
    phone: '',
    address1: '',
    address2: '',
    memo: '',
  
// purchase λ‚΄ address component
const Address = ({
  inputValue,
  setInputValue,
}) => {
  const { name, phone, address1, address2 } = inputValue
  
  const handleUserInput = e => {
    const { name, value } = e.target
    setInputValue({ ...inputValue, [name]: value })
  }
  

λ¨Όμ € purchase νŽ˜μ΄μ§€μ— address λͺ¨λ‹¬μ—μ„œ 받은 input valueλ₯Ό 좜λ ₯ν•  것이기 λ•Œλ¬Έμ—
purchase νŽ˜μ΄μ§€μ— inputValue와 setInputValue stateλ₯Ό addressμ—μ„œ λ°›μ•„μ„œ μ‚¬μš©ν–ˆλ‹€.

address λ‚΄λΆ€μ—μ„œλŠ” inputValueλ₯Ό 비ꡬ쑰화 할당을 톡해 name, phone, address1, address2 둜 λΆ„ν•΄ν•˜κ³  이λ₯Ό 톡해 각각의 값을 μ €μž₯ν•˜κ³  μΆ”μ ν•˜λ„λ‘ ν–ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ handleUserInput ν•¨μˆ˜λŠ” input의 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” ν•¨μˆ˜μΈλ°,
eλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›μ•„ e.target의 nameκ³Ό valueλ₯Ό ꡬ쑰뢄해할당을 μ΄μš©ν•΄ μΆ”μΆœν•œλ‹€.

κ·Έ ν›„ μΆ”μΆœν•œ 값을 λ‹€μ‹œ setInputValue ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ inputValue의 μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈν•˜λŠ”λ°
이 λ•Œ μŠ€ν”„λ ˆλ“œ 문법(μ „κ°œμ—°μ‚°μž) λ₯Ό μ‚¬μš©ν•˜μ—¬ 볡사후 [name]: valueλ₯Ό 톡해 ν•΄λ‹Ή μž…λ ₯ μš”μ†Œμ˜ 값을 μ—…λ°μ΄νŠΈ ν•˜λ„λ‘ ν–ˆλ‹€.

10. ꡬ쑰뢄해 ν• λ‹Ή 및 전체 λ™μ˜ state 관리

const [agreeList, setAgreeList] = useState({
    isInfoAgree: false,
    isUseAgree: false,
  })

  const { isInfoAgree, isUseAgree } = agreeList

  const isAllChecked = Object.values(agreeList).every(list => list === true)
  
  const handleAgree = name => {
    setAgreeList(prev => ({ ...prev, [name]: !prev[name] }))
  }

  const handleAllCheck = () => {
    if (isAllChecked) {
      setAgreeList(prev => ({ ...prev, isInfoAgree: false, isUseAgree: false }))
    } else {
      setAgreeList(prev => ({ ...prev, isInfoAgree: true, isUseAgree: true }))
    }
  }

λ™μΌν•œ 약관에 λŒ€ν•œ λ™μ˜κ°€ 쒌츑 ν•˜λ‹¨κ³Ό 우츑 상단 λ‘λ²ˆμ— 걸쳐 좜λ ₯λ˜μ–΄μžˆλ‹€.
λ”°λΌμ„œ 쒌츑의 λ™μ˜μ™€ 우츑의 λ™μ˜κ°€ μ—°λ™λ˜μ–΄ κ΄€λ¦¬λ˜λ„λ‘ ν•˜λ©΄μ„œ, 결과적으둜 4개의 λ™μ˜κ°€ λͺ¨λ‘ μ²΄ν¬λ˜μ—ˆμ„ λ•Œ
전체 λ™μ˜ μ²΄ν¬λ°•μŠ€μ— 체크가 μžλ™μœΌλ‘œ λ“€μ–΄μ˜€κ²Œ ν•˜κ³ ,
전체 λ™μ˜ μ²΄ν¬λ°•μŠ€λ₯Ό λˆ„λ₯΄λ©΄ λͺ¨λ‘μ— 체크가 λ“€μ–΄μ˜€κ±°λ‚˜ λͺ¨λ‘ μ‚¬λΌμ§€κ²Œ ν•˜κ³  μ‹Άμ—ˆλ‹€.

λ¨Όμ € agreeList stateλ₯Ό isInfoAgree 와 isUseAgree 둜 객체 ν˜•νƒœλ‘œ κ΅¬μ„±ν–ˆλ‹€.
그런 λ’€ agreeListλ₯Ό isInfoAgree와 isUseAgree둜 ꡬ쑰뢄해 할당을 ν•΄ μ‚¬μš©ν•˜κΈ° νŽΈν•˜κ²Œ λ§Œλ“€μ—ˆλ‹€.

isAllCheckedλŠ” Object.values(agreeList) λ₯Ό μ‚¬μš©ν•˜μ—¬ 객체값을 λ°°μ—΄λ‘œ λ³€ν™˜ν•˜κ³ 
every λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ list λ‚΄μ˜ λͺ¨λ“  값을 μ£Όμ–΄μ§„ 쑰건인 true에 λΆ€ν•©ν•˜λŠ” μ§€ μ²΄ν¬ν•˜μ—¬ μ €μž₯ν•˜λ„λ‘ ν–ˆλ‹€.

handleAgreeλŠ” name을 λ§€κ°œλ³€μˆ˜λ‘œ agreeList 객체 λ‚΄μ˜ 각각의 속성을 μΆ”μ ν•˜μ—¬ μ²΄ν¬λ°•μŠ€μ˜ μƒνƒœλ₯Ό κ°œλ³„μ μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ handleAllcheckλŠ” isAllchecked의 λ°˜ν™˜κ°’μ„ ν™•μΈν•˜μ—¬
λ§Œμ•½ true인 μƒνƒœλΌλ©΄ λͺ¨λ‘ false둜 λ³€ν™˜ν•˜μ—¬ λͺ¨λ“  μ²΄ν¬λ°•μŠ€λ₯Ό 끄도둝 ν•˜κ³ 
λ§Œμ•½ false인 μƒνƒœλΌλ©΄ λͺ¨λ‘ true둜 λ³€ν™˜ν•˜μ—¬ λͺ¨λ“  μ²΄ν¬λ°•μŠ€λ₯Ό 킀도둝 ν•œλ‹€.

  • Object.values() : μ „λ‹¬λœ νŒŒλΌλ―Έν„° 객체가 κ°€μ§€λŠ” μ†μ„±μ˜ κ°’λ§ŒμœΌλ‘œ 이루어진 배열을 λ¦¬ν„΄ν•œλ‹€.
  • .every : λ°°μ—΄ λ‚΄ λͺ¨λ“  μš”μ†Œκ°€ μ£Όμ–΄μ§„ νŒλ³„μ‹μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•˜μ—¬ boolean 값을 λ°˜ν™˜ν•œλ‹€.

11. reduce λ©”μ„œλ“œλ‘œ 총 κΈˆμ•‘ 계산 및 관리

  const totalPrice = orderList.reduce(
    (acc, cur) => acc + cur.itemQuantity * cur.itemPrice,
    0
  )

orderListλŠ” ꡬ맀할 μƒν’ˆλ“€μ˜ 각각의 가격과 ꡬ맀 μˆ˜λŸ‰μ„ ν¬ν•¨ν•œ 배열이닀.
reduceλ‚΄μ˜ accλŠ” κΈ°μ‘΄ κ°’, cur은 ν˜„μž¬ 값을 μ˜λ―Έν•œλ‹€. reduceλŠ” λ°°μ—΄μ˜ λͺ¨λ“  μš”μ†Œλ₯Ό μˆœνšŒν•˜λ©°
μ •ν•΄μ§„ ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜κ³  이전에 μ‹€ν–‰ν•΄μ„œ λ„μΆœν•΄λ‘μ—ˆλ˜ κ°’, acc에 ν•©μ‚°ν•œλ‹€. λͺ¨λ“  λ°°μ—΄ μš”μ†Œμ— μˆ˜ν–‰μ΄ λλ‚˜λ©΄ ν•˜λ‚˜μ˜ 값을 λ„μΆœν•΄ λ°˜ν™˜ν•œλ‹€.

  • reduce : λ°°μ—΄μ˜ 각 μš”μ†Œμ— λŒ€ν•΄ μ •ν•΄μ§„ ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜κ³  ν•˜λ‚˜μ˜ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•œλ‹€.

0개의 λŒ“κΈ€