fix ui issue - boostcampwm-2022/web33-Mildo GitHub Wiki

โœ‚๏ธ ๋ถ„๋ฐฐ๋œ ์ด์Šˆ

UI ์ด์Šˆ ํ•ด๊ฒฐ (#68, https://github.com/boostcampwm-2022/web33-Mildo/issues/86)

๐Ÿšฉ ๊ตฌํ˜„ ๋ชฉํ‘œ

์ƒ์„ธ๋ชจ๋‹ฌ์ฐฝ ์ œ๋ชฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€๊ฒฝ (v)

๊ฒ€์ƒ‰์ฐฝ ํฌ์ปค์Šค ์‹œ ์•„์›ƒ๋ผ์ธ ํ•ด์ œ

๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ์ฐฝ, ์‚ฌ์ด๋“œ๋ฐ” ๋ชจ๋‹ฌ์ฐฝ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ

๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ ๋ชจ๋‹ฌ ๋„ˆ๋น„ ๊ธธ์ด ์กฐ์ ˆ

๐Ÿ€ ์„ธ๋ถ€ ๋ชฉํ‘œ

๐Ÿ–ฅ๏ธ ๊ตฌํ˜„ ๋‚ด์šฉ

์ƒ์„ธ๋ชจ๋‹ฌ์ฐฝ ์ œ๋ชฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€๊ฒฝ

export const Title = styled.h1`
  ...
  /* width: 100%; */
	...
`;
  • width: 100% ์ œ๊ฑฐ โ†’ ์ด๊ฑธ๋กœ ์ธํ•ด offsetWidth, clientWidth, scrollWidth์ด ๋ชจ๋‘ ๊ฐ™์€ px์ด ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์ œ๋ชฉ์˜ width๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์ธก์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.
const titleWidthRef = useRef<HTMLHeadingElement>(null);
...
useEffect(() => {
  if (titleWidthRef && titleWidthRef.current) {
    setTitleWidth(titleWidthRef.current.clientWidth);
  }
}, [firstLevelInfo]);
  • titleWidthRef ์†์„ฑ์„ Title ์—˜๋ฆฌ๋จผํŠธ์— ์„ค์ •ํ•˜์—ฌ, ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ์— ๋Œ€์‘ํ•˜๋Š” width๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
  • ์›๋ž˜๋Š” useLayoutEffect๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ–ˆ๋‹ค. useLayoutEffect๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ํ™”๋ฉด ๊นœ๋นก์ž„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ๊ณผ, ๋ Œ๋”๋ง ์‹œ์ ์—์„œ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์†๋„๊ฐ€ ๋А๋ ค์ง„๋‹ค๋Š” ๋‹จ์ ์„ ์•ˆ๊ณ  ์žˆ๋‹ค. ์†๋„์˜ ๋А๋ฆผ ํ˜„์ƒ์„ ์œ ์˜๋ฏธํ•˜๊ฒŒ ์ฒด๊ฐํ•˜์ง€๋Š” ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋‹จ์€ useEffect๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ–ˆ๋‹ค. (์ฐธ๊ณ  ์ž๋ฃŒ: https://guiyomi.tistory.com/120)
const [windowWidth, setWindowWidth] = useState<number>(window.innerWidth);

useEffect(() => {
    const checkViewportWidth = () => {
      setWindowWidth(window.innerWidth);
    };

    window.addEventListener('resize', checkViewportWidth);

		// ํด๋ฆฐ์—… ํ•จ์ˆ˜
    return () => {
      window.removeEventListener('resize', checkViewportWidth);
    };
  }, []);
  • ๊ทธ ๋‹ค์Œ์œผ๋กœ๋Š”, ๋ทฐํฌํŠธ(ํ™”๋ฉด ์ „์ฒด)์˜ ๋„ˆ๋น„๋ฅผ ๊ตฌํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. window.innerWidth๋กœ ์ดˆ๊ธฐํ™”๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.
  • ํด๋ฆฐ์—… ํ•จ์ˆ˜๋ฅผ ๋„ฃ์—ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์žฌ๋ Œ๋”๋ง์ด ๋˜๋Š” ์‹œ์ ๊ณผ ์ƒˆ๋กœ์šด useEffect ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์‹œ์  ์‚ฌ์ด์— ์‹คํ–‰๋œ๋‹ค. ๋”ฐ๋ผ์„œ โ€˜resize'์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฉด checkViewportWidth โ†’ setWindowWidth๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด window.innerWidth์œผ๋กœ ๊ฐ’์„ ๊ฐฑ์‹ ์‹œ์ผœ ์ฃผ๊ณ , ์žฌ๋ Œ๋”๋ง ๋œ๋‹ค.
  • โ€˜ํด๋ฆฐ์—… ํ•จ์ˆ˜โ€™๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•œ๋‹ค.
const [slidable, setSlidable] = useState<boolean>(true);
...
useEffect(() => {
  if (titleWidth + 50 > windowWidth) {
    setSlidable(true);
    return;
  }
  setSlidable(false);
}, [titleWidth, windowWidth]);
  • ์ด์ œ titleWidth, windowWidth๋ฅผ ์ „๋ถ€ ๊ฑธ์–ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ ๋‹ค์Œ์—๋Š” slidable์†์„ฑ์„ ๊ฑธ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. titleWidth, windowWidth์˜ ์ƒํƒœ๊ฐ’์ด ๋ณ€ํ™”ํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ”๋€” ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•˜์˜€์œผ๋ฉฐ, ๋งŒ์•ฝ์— ์ œ๋ชฉ์ด ๋„ˆ๋ฌด ๊ธธ๋‹ค๊ณ  ํŒ๋‹จ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ๊ฑธ์–ด๋‘๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ๊ฑธ์ง€ ์•Š๋„๋ก, **setSlidable()**์„ ํ†ตํ•ด์„œ ๊ฐ’์„ ์„ค์ •ํ–ˆ๋‹ค.
<Title ref={titleWidthRef} slide={slidable} textWidth={titleWidth}>
  ํ˜„์žฌ&nbsp;
  <TitleLocation
    populationLevel={firstLevelInfo[1].populationLevel}>
    {firstLevelInfo[0]}
  </TitleLocation>
  {INFO_DETAIL_TITLE[firstLevelInfo[1].populationLevel]}
</Title>
{slidable ? (
  <Title
    ref={titleWidthRef}
    slide={slidable}
    textWidth={titleWidth}>
    ํ˜„์žฌ&nbsp;
    <TitleLocation
      populationLevel={firstLevelInfo[1].populationLevel}>
      {firstLevelInfo[0]}
    </TitleLocation>
    {INFO_DETAIL_TITLE[firstLevelInfo[1].populationLevel]}
  </Title>
) : (
  <></>
)}

export const Title = styled.h1<TitleTypes>`
  display: block;
  text-align: center;
  font-size: 1rem;
  white-space: nowrap;
  will-change: transform;
  animation: ${props =>
    props.slide
      ? css`
          ${marquee(props.textWidth)} 10s linear infinite
        `
      : ''};
`;
  • ์ด์ œ slidable ์ƒํƒœ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ Title์—์„œ slide props๋กœ ๋„˜๊ฒจ์ฃผ๊ณ , styled-component์—์„œ slide์˜ ๊ฐ’์— ๋”ฐ๋ผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ตฌํ˜„ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•œ๋‹ค.
  • textWidth props๋ฅผ ํ†ตํ•ด marquee ์†์„ฑ์„ ์ ์šฉํ•  ๋•Œ, ํ•ด๋‹น ํ…์ŠคํŠธ ๊ธธ์ด์— ๋งž๋Š” ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ…์ŠคํŠธ ๊ธธ์ด ์—ฌ๋ถ€์— ์ƒ๊ด€์—†์ด ๋ชจ๋‘๊ฐ€ ๋™์ผํ•œ ์œ„์น˜์—์„œ ๋™์ผํ•œ ์†๋„๋กœ ์ด๋™ํ•˜๊ฒŒ ๋œ๋‹ค.
  • ๊ฒฐ๋ก : keyframes์˜ transform: translate() ์†์„ฑ์˜ x๊ฐ’์„ ํ•ด๋‹น ํ…์ŠคํŠธ ๊ธธ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค์ •ํ•˜๊ณ , flexbox์˜ gap ์†์„ฑ์„ 50%๋กœ ์„ค์ •, ๋งˆ์ง€๋ง‰์œผ๋กœ ๋‘ ๊ฐœ์˜ Title ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ Œ๋”๋ง ์‹œ์ผœ์ฃผ๋ฉด ๊ฐ€์žฅ ์ตœ์ ํ™”๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์™„์„ฑ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

๊ฒ€์ƒ‰์ฐฝ ํฌ์ปค์Šค ์‹œ ์•„์›ƒ๋ผ์ธ ํ•ด์ œ

&:focus {
    outline: none;
}
  • ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ์— focus ์ด๋ฒคํŠธ ์ ์šฉํ•  ์‹œ outline: none ์„ ์ ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ•ด๊ฒฐ.

๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ์ฐฝ, ์‚ฌ์ด๋“œ๋ฐ” ๋ชจ๋‹ฌ์ฐฝ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ

  • ๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ๊ณผ ์‚ฌ์ด๋“œ๋ฐ”์˜ ๋ชจ๋‹ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋‹ค๋ฅด๊ฒŒ ์ ์šฉํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ, ๊ฐ๊ฐ ๋”ฐ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•˜๋Š” ์ผ์ด ์ƒ๊ฐ๋ณด๋‹ค ๊ฝค ๊ท€์ฐฎ์€ ์ผ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ•˜๊ณ ์ž ํ–ˆ๋‹ค.
return (
    <>
      {/* {isOpen && (
        <>
          {background && <Filter onClick={clickModalHandler} />}
          <ModalContainer customModalStyle={customModalStyle}>
            {children}
          </ModalContainer>
        </>
      )} */}
      <Filter onClick={clickModalHandler} open={isOpen && background} />
      <ModalContainer customModalStyle={customModalStyle} open={isOpen}>
        {children}
      </ModalContainer>
    </>
  );
};
  • ์ผ๋‹จ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ๊ฑท์–ด๋ƒˆ๋‹ค. ํŠนํžˆ, ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ณด์—ฌ์ฃผ๋ ค๊ณ  ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ฑฐ๋Š” ๊ฒฝ์šฐ์—๋Š” display: none์„ ๊ฑธ๊ฑฐ๋‚˜ ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋‚˜์ค‘์— display ์†์„ฑ์„ ๊ฑธ์–ด์ค„ ์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ ๋Œ€์‹ ์—, Filter ์ปดํฌ๋„ŒํŠธ์™€ ModalContainer์— open prop์„ ์ถ”๊ฐ€ํ•ด์„œ, ํ•ด๋‹น prop์˜ boolean ์œ /๋ฌด์— ๋”ฐ๋ผ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ๊ฒƒ์ธ ์ง€์— ๋Œ€ํ•ด์„œ ์ž‘์„ฑํ•  ์˜ˆ์ •์ด์—ˆ๋‹ค.
const ModalContainer = styled.div<ModalContainerProps>`
  z-index: ${Z_INDEX.MODAL};
  ${props => props.customModalStyle && props.customModalStyle}
  /* display: ${props => !props.open && 'none'}; */
  visibility: ${props => (props.open ? 'visible' : 'hidden')};
  ${props => !props.open && `z-index: -100`};
  animation: ${props =>
    props.open
      ? css`
          ${fadeIn} .2s linear
        `
      : css`
          ${fadeOut} .2s linear
        `};
`;

const Filter = styled.div<{ open: boolean }>`
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: ${Z_INDEX.FILTER};
  background-color: rgba(0, 0, 0, 0.5);
  /* display: ${props => (props.open ? 'block' : 'none')}; */
  visibility: ${props => (props.open ? 'visible' : 'hidden')};
	${props => !props.open && `z-index: -100`};
  animation: ${props =>
    props.open
      ? css`
          ${fadeIn} .2s linear
        `
      : css`
          ${fadeOut} .2s linear
        `};
`;
  • ๊ทธ ๋‹ค์Œ์œผ๋กœ๋Š” display: none ์†์„ฑ์„ ์‚ญ์ œํ–ˆ๋‹ค. ํ•ด๋‹น ์†์„ฑ์„ ์ ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ ๋Œ€์‹ ์—, visibility ์†์„ฑ๊ณผ z-index ์†์„ฑ์„ ์ ์šฉํ•ด์„œ ์•ˆ๋ณด์ด๊ฒŒ๋” ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•œ ๋‹ค์Œ, ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ง„ํ–‰๋จ์˜ ์—ฌ๋ถ€์— ๋”ฐ๋ผ์„œ ๋ณ€ํ™”๋ฅผ ์ฃผ๊ธฐ๋กœ ์ƒ๊ฐํ–ˆ๋‹ค.
  • ์• ๋‹ˆ๋ฉ”์ด์…˜์€ @keyframes ๋ฅผ ์ ์šฉํ•˜๊ณ ์ž ๋…ธ๋ ฅํ•˜์˜€๊ณ , open prop์˜ boolean ์†์„ฑ ์—ฌ๋ถ€์— ๋”ฐ๋ผ fadeIn ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ถ€์—ฌํ•  ์ง€, fadeOut ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ถ€์—ฌํ•  ์ง€์— ๋Œ€ํ•œ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค.
const fadeIn = keyframes`
  0% {
    opacity: 0;
    display: block;
  } 
  100% { 
    opacity: 1;
  }
`;

const fadeOut = keyframes`
  0% {
    opacity: 1;
    display: block;
  } 
  100% { 
    opacity: 0;
  }
`;
  • ๋งˆ์ง€๋ง‰์œผ๋กœ fadeIn ์˜ต์…˜๊ณผ fadeOut ์˜ต์…˜์„ ์‚ดํŽด๋ณด๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด @keyframes ๋ฅผ ์ ์šฉํ•ด์„œ ๋‚˜ํƒ€๋‚ด๊ธฐ๋กœ ํ–ˆ๋‹ค.

๊ฒ€์ƒ‰ ์ž๋™์™„์„  ๋ชจ๋‹ฌ ๋„ˆ๋น„ ๊ธธ์ด ์กฐ์ ˆ

์ด ๋ถ€๋ถ„์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์€ โ€˜์ƒ์„ธ๋ชจ๋‹ฌ์ฐฝ ์ œ๋ชฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€๊ฒฝโ€™๊ณผ ํก์‚ฌํ•˜๊ฒŒ **useRef()**๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

...

const searchBarWidthRef = useRef<HTMLInputElement>(null);
const [searchBarWidth, setSearchBarWidth] = useState(0);

...

useEffect(() => {
    const checkSearchBarWidth = () => {
      if (searchBarWidthRef && searchBarWidthRef.current!.clientWidth <= 439) {
        setSearchBarWidth(searchBarWidthRef.current!.clientWidth);
      }
    };

    window.addEventListener('resize', checkSearchBarWidth);

    return () => {
      window.removeEventListener('resize', checkSearchBarWidth);
    };
  }, []);

...

return (
    <FlexBoxStyle>
      <SearchBar
        placeholder='๊ฒ€์ƒ‰'
        onChange={onChangeSearchBar}
        value={searchAreaName}
        ref={searchBarWidthRef}
      />
      <MyButton
        onClick={onClickMyButton}
        dangerouslySetInnerHTML={{
          __html: createMyButtonSvg()
        }}></MyButton>
      <RelatedAreaList
        searchAreaName={searchAreaName}
        relatedAreaInfo={relatedAreaInfo}
        widthValue={searchBarWidth}
      />
    </FlexBoxStyle>
  );
  • searchBarWidthRef๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. ์ด ref๋Š” SearchBar ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜์–ด์ง€๋Š” ref๋กœ, ๋„ˆ๋น„๋ฅผ ์ธก์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด ref ๊ฐ’์„ SearchBar์˜ ref๋กœ ๋“ฑ๋กํ•˜๊ฒŒ ๋˜๋ฉด, searchBarWidthRef๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ’์„ ๊ตฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • **useEffect()**๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด์ „๊ณผ ๋น„์Šทํ•˜๊ฒŒ ์ด๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. window.addEventListener๋ฅผ ์ด์šฉํ•ด์„œ โ€˜resizeโ€™ ์ด๋ฒคํŠธ๋ฅผ ๊ฑธ๋ฉด, checkSearchBarWidth ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ๊ฒ€์ƒ‰๋ฐ”์˜ ์ตœ๋Œ“๊ฐ’์ด 439ํ”ฝ์…€์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ setSearchBarWidth() ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ณ  ์ƒํƒœ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค. ์ด ๋•Œ, ๊ฐ’์„ ๋ฐ”๊พธ๋Š” ๊ธฐ์ค€์€ ๋ฐ”๋กœ 'refโ€™๋ฅผ ๊ฑด ๋Œ€์ƒ์˜ width ๊ฐ’์ด๋‹ค.
  • RelatedAreaList ์—˜๋ฆฌ๋จผํŠธ์— widthValue ์†์„ฑ๊ฐ’์„ ๋‚ด๋ ค์ค€๋‹ค. searchBarWidth๋Š” setSearchBarWidth() ํ•จ์ˆ˜๋กœ ์ธํ•˜์—ฌ ๋ฐ”๋€Œ์–ด์ง€๋Š” ๊ฐ’์ด๋‹ค.
...

const RelatedAreaListStyle = styled.ul<{ width: number }>`
  width: ${props => props.width}px;
  max-width: 439px;
  max-height: 15rem;
  overflow: auto;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: absolute;
  top: 3.5rem;
  background-color: white;
  border-radius: 10px;
  border: none;
  box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.25);
  list-style: none;
`;

...

return (
    <>
      {isRelatedAreaListOpen && (
        <RelatedAreaListStyle
          onClick={onClickRelatedAreaList}
          width={widthValue}>
          {isEmptyRelatedList ? (
            <RelatedAreaItem
              searchAreaName={searchAreaName}
              areaInfo={emptyAreaInfo}
            />
          ) : (
  • RelatedAreaListStyle ์—˜๋ฆฌ๋จผํŠธ์—์„œ ๋ฐ›์•„์˜จ widthValue๋ฅผ width๋กœ ๋‚ด์—ฌ์ฃผ๋ฉด, styled-components์—์„œ width ๊ฐ’์„ ๊ฒ€์ƒ‰์ฐฝ ๋ฐ”์™€ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๐Ÿ“– ํ•™์Šต ๋‚ด์šฉ

์ƒ์„ธ๋ชจ๋‹ฌ์ฐฝ ์ œ๋ชฉ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ณ€๊ฒฝ

  • ํ˜„์žฌ **์ƒ์„ธ๋ชจ๋‹ฌ(1๋‹จ๊ณ„)**์—์„œ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ๋Š” ์ œ๋ชฉ ๊ด€๋ จ ๋ถ€๋ถ„์ด ํ™”๋ฉด์ด ์ž‘์€ ๋ชจ๋ฐ”์ผ์—์„œ๋Š” ์ž˜๋ ค์ ธ์„œ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋ฅผ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ™”ํ•ด์„œ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ™”๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด ์ข‹์„ ์ง€ ์ฐพ๋˜ ์ค‘์— ๋‹ค์Œ์˜ ๋งํฌ๋ฅผ ํ†ตํ•ด margin-left, margin-right ์†์„ฑ์„ ๋ถ€์—ฌํ•ด์„œ ๋ณด์—ฌ์ฃผ๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.
  • animation: 12s autoplay3 infinite linear
    • infinite: ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋ฌดํ•œํžˆ ๋ฐ˜๋ณตํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•œ๋‹ค.
    • linear: ์„ ํ˜•์ ์œผ๋กœ ์›€์ง์ผ ์ˆ˜ ์žˆ๊ฒŒ๋” ์„ค์ •ํ•œ๋‹ค. linear ์†์„ฑ์„ ์ ์šฉํ•˜๋ฉด ํ•ญ์ƒ ์ผ์ •ํ•˜๊ฒŒ ์›€์ง์ผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
  • offsetWidth, clientWidth, scrollWidth
    • ์ผ๋‹จ, **vw(viewport-width)**์™€ ํ˜„์žฌ ์ œ๋ชฉ์˜ width ์†์„ฑ์„ ๋™์‹œ์— ๊ตฌํ•œ ๋‹ค์Œ, ์ด๋“ค ๊ฐ„์˜ ๊ธธ์ด๋ฅผ ๋น„๊ตํ•ด์„œ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํšจ๊ณผ๋ฅผ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ์— vw๊ฐ€ ๋” ํฐ ๊ฒฝ์šฐ์—๋Š” ๊ตณ์ด overflow๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ๋งŒ์•ฝ์— ์ œ๋ชฉ์˜ width๊ฐ€ ๋” ํฐ ๊ฒฝ์šฐ์—๋Š” ์ œ๋ชฉ์ด ์ž˜๋ ค์„œ ๋ณด์—ฌ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
    • ์ด ์„ธ๊ฐ€์ง€ width ์†์„ฑ์ด ๋ชจ๋‘ ๋‹ค๋ฅด๊ณ , ์ด๋“ค ์ค‘์— ์–ด๋–ค width ์†์„ฑ์„ ์ ์šฉํ•ด์•ผ ํ•  ์ง€ ๊ณ ๋ฏผ๋˜์—ˆ๋‹ค.
      • ์ผ๋ฐ˜์ ์œผ๋กœ ์—˜๋ฆฌ๋จผํŠธ์˜ ์ „์ฒด ํฌ๊ธฐ๋ฅผ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, offsetWidth์™€ offsetHeight ์†์„ฑ
      • ์‹ค์ œ๋กœ ๋ณด์—ฌ์ง€๊ณ  ์žˆ๋Š” ์ปจํ…์ธ ๊ฐ€ ์–ผ๋งˆ๋งŒํผ์˜ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, clientWidth์™€ clientHeight ์†์„ฑ
      • ๋ณด์ด๋Š” ๊ฒƒ๊ณผ ์ƒ๊ด€ ์—†์ด ์‹ค์ œ ์ปจํ…์ธ  ์˜์—ญ์ด ์–ผ๋งˆ๋งŒํผ์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, scrollWidth์™€ scrollHeight ์†์„ฑ
  • useLayoutEffect
    • useEffect์™€ ์—ฌ๋Ÿฌ ๋ชจ๋กœ ๋น„์Šทํ•ด ๋ณด์ด์ง€๋งŒ, ๋‘˜ ๊ฐ„์˜ ์ฐจ์ด๋Š” ๋ถ„๋ช…ํžˆ ์กด์žฌํ•œ๋‹ค.
      • useEffect๋Š” ๋ Œ๋”๋ง์„ ์ง„ํ–‰ํ•˜๊ณ  ํŽ˜์ธํŠธ๋œ ์ดํ›„์— ์‹คํ–‰๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์ž‘์—…์ด ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, useLayoutEffect๋Š” ๋ Œ๋”๋ง๊ณผ ํŽ˜์ธํŒ… ์‚ฌ์ด์— ์‹คํ–‰๋˜๋ฉฐ ์ด ์ž‘์—…์€ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
โš ๏ธ **GitHub.com Fallback** โš ๏ธ