학습 내용 정리

styled-component global style,vscode, 드랍다운 메뉴, input focuse 핸들링, 문자열 앞뒤 공백 제거

styled-components 글로벌스타일

스타일드 컴포넌츠에서 전역 스타일을 줄 때 createGlobalStyle을 사용하여 주었습니다.

import { createGlobalStyle } from 'styled-components'

const GlobalStyle = createGlobalStyle`
  body {
    color: ${props => (props.whiteColor ? 'white' : 'black')};
  }
`

<React.Fragment>
  <GlobalStyle whiteColor />
  <Navigation /> {/* example of other top-level stuff */}
</React.Fragment>

이번에 다시 스타일드 컴포넌트를 사용하며 공식문서를 확인하는데, V5를 오며 createGlobalStyle을 사용하는것을 권장하지 않는다고 나와있습니다. 브라우저가 CSSOM APIs를 불러오는 과정에 몇가지 이슈가 있다고 하여 그렇다는데, 대체 솔루션으로 index.html파일에 style 태그로 대체할 수 있다고 합니다.

...
<style>
  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    list-style: none;
    outline: none;
    text-decoration: none;
    border: 0;
    font-family: 'Nanum GoThic';
  }
  body {
    background: rgb(242, 242, 245);
  }
</style>
...

위와 같이 기본적인 reset.css를 적용해 줄 수 있습니다.

vscode 옵셔널체이닝 syntax error

vscode에서 js옵셔널 체이닝을 사용하는데 자꾸 신텍스에러가 생겼습니다. 문법적 문제도 아니고 생각해보다 vscode 버전이 낮아서 업데이트가 안되고 있다는것을 알았습니다. 그래서 왜 vscode 자동 업데이트가 안될까? 알아보았는데 vscode가 맥북 읽기전용 폴더에 있어서 자동업데이트가 안되는것이었습니다. 그래서 읽기전용 폴더에서 위치를 바꿔줍니다.

vscode 디렉토리를

응용프로그램 (사용자와 같은 디렉토리에 있는)
위의 응용프로그램에 옮겨주니 업데이트가 적용이되고 신텍스에러가 사라졌습니다.

드랍다운 메뉴바

프로젝트를 진행하며 네비게이션 바에서 마우스를 hover하면 드랍다운이 되는 메뉴바를 구현하고 있는데, 구현하며 onmouseenter 이벤트와 onmouseleave 이벤트를 사용하였습니다.

...
const [hoverLabel, setHoverLabel] = useState('');

  const goToLogin = () => {
    history.push('/Login');
  };

  return (
    ...
          <BottomContext>
            {LABEL_LIST.map((el) => (
              <Label
                onMouseEnter={() => {
                  if (el.subLabel) {
                    setHoverLabel(el.title);
                  }
                }}
                onMouseLeave={() => {
                  if (el.subLabel) {
                    setHoverLabel('');
                  }
                }}
                onClick={() => {
                  history.push(el.path);
                }}
                key={el.id}
              >
                <LabelText current={history.location.pathname === el.path}>
                  {el.title}
                </LabelText>
                {hoverLabel === el.title && (
                  <SubLabelContainer>
                    {el.subLabel?.map((el) => (
                      <SubLabelList
                        onClick={(e) => {
                          e.stopPropagation();
                          history.push(el.path);
                        }}
                        key={el.id}
                      >
                        {el.name}
                      </SubLabelList>
                    ))}
                  </SubLabelContainer>
                )}
              </Label>
            ))}
          </BottomContext>
        ...

UI는 해당 이벤트를 주어 적용이 되었는데, 여기서 생긴 문제가 자식요소에도 history.push를 하고 부모요소에서도 history.push를 하는데, 부모 push만 적용이 되었습니다. 그래서 이벤트 전파때문에 그런것같아서 자식 history.push하는 클릭 이벤트에서

onClick={(e) => {
             e.stopPropagation();
             history.push(el.path);
                  }}

e.stopPropagation으로 전파를 막아주고 문제를 해결했습니다.

배열 중복요소 제거

배열 중복제거를 한줄로 요약하는 방법을 찾아보다가 알게된 방법입니다.

arr.filter((item, idx) => arr.indexOf(item) === idx)

위의 방법을 적용해보면

function removeOverlap(arr) {
  return arr.filter((item, idx) => arr.indexOf(item) === idx)
}

removeOverlap([1, 2, 3, 4, 5, 6, 1, 2, 3])
// [1,2,3,4,5,6]

removeOverlap(['하나', '둘', '셋', '하나'])
// ['하나', '둘', '셋']

중복이 제거가됩니다.

input focus 핸들링 하기

까다로운 인풋 포커스를 핸들링 하며 과정을 정리해보았습니다.
인풋이 렌더링 될 때 포커스를 주려면 autofocus 속성을 사용합니다.

<Input autoFocus />

그리고 포커스가 될 때, 보더 색상을 바꿔주고 싶었는데, 처음에는 css에서 &:focus 선택자로 보더를 줄려고 했는데 원하는 결과가 나오지 않았습니다.

<div>
  <div />
  <input />
  <img />
</div>

위와같은 구조라고 한다면, &:focus는 딱 input 요소에만 적용이 되고, 인풋 요소가 포커스가되면 부모 div요소 에 적용을 하고 싶었습니다. 그래서 js를 이용해 핸들링을 해야하나 고민을 하고 있었는데, css에 focus-within 이라는 것을 알았습니다.

const SearchBoxWrapper = styled.form`
  ... border: 1px solid transparent;

  &:focus-within {
    border: 1px solid #a3e4d7;
  }
`

위의 속성을 적용하면 내부에 있는 인풋이 포커스 될 때, 부모 요소의 스타일을 바꿔 주는게 가능합니다.

인풋 클릭 시 드랍다운 요소가 생기며 요소 바깥 클릭 시 드랍다운 요소가 사라지는 기능을 구현했습니다.
처음에는 onFocus와 onBlur 이벤트로 상태변경을 하여 나타나게 했는데, onFocus는 첫 autofocus에서 포커스가 될때에도 작동을 하기때문에 onClick으로 상태를 관리했는데, 문제는 onBlur에서 상태를 false로 바꿔주면 자식 드랍다운 요소를 클릭할때도 blur가 적용되어 사라졌습니다. 그래서 react docs를 보다가 좋은 예를 찾아서 적용을 했습니다.

function SearchInput({ history }) {
  const [focused, setFocused] = useState(false)

  return (
    <SearchBoxWrapper onSubmit={handleSubmit}>
      <Input
        onClick={() => {
          setFocused(true)
        }}
        autoFocus
      />
      <SearchIconWrapper
        onClick={e => {
          handleSubmit(e)
          e.preventDefault()
        }}
      >
        <SearchIcon />
      </SearchIconWrapper>
      {focused && <RecentSearch setFocused={setFocused} focused={focused} />}
    </SearchBoxWrapper>
  )
}

위처럼 클릭하면 해당 컴포넌트가 나오게 해주고,

function RecentSearch() {

  const toggleContainer = useRef(null);

  const onClickHandler = (e) => {
    if (focused && !toggleContainer.current.contains(e.target)) {
      setFocused(false);
    }
  };

  useEffect(() => {
    window.addEventListener('click', onClickHandler);

    return () => {
      window.removeEventListener('click', onClickHandler);
    };
  }, []);

  return (
    <RecentSearchBox ref={toggleContainer} focused={focused}>
    ...
  )
  ...
}

자식요소에서 window 클릭이벤트를 주어 자식 드랍다운 요소를 ref로 잡아주고 클릭시 해당 요소가 아니라면(바깥 클릭 시) false로 핸들링 하는 방법을 적용해 주었습니다. 공식 문서를 보다가 ref.current.contain()로 포함하는지 체크를 할 수 있는 방법도 알게 되었습니다. https://ko.reactjs.org/docs/accessibility.html 리액트 문서를 참조하면 키보드 사용자 등을 위해 더 좋은 사용자 경험을 줄 수 있는 방법도 소개해주고있는데, 적용하다가 적용이 되지 않아서 위의 방법을 적용하였습니다.

문자열 앞뒤 공백 제거

이번에 자바스크립트 문자열 앞뒤 공백 제거하는 메소드를 찾았습니다.
.trim() 메소드를 사용하면 문자 공백이 제거가됩니다.

const 문자열 = '인녕'
const 앞공백 = ' 안녕'
const 뒤공백 = '안녕 '
const 앞뒤공백 = ' 안녕 '
const 많은공백 = '   안녕  '
const 가운데공백 = '안 녕'

문자열.trim() // '안녕'
앞공백.trim() // '안녕'
뒤공백.trim() // '안녕'
앞뒤공백.trim() // '안녕'
많은공백.trim() // '안녕'
가운데공백.trim() // '안 녕'

정확하게 앞뒤에있는 공백만 모두 제거해 주는것을 확인할 수 있습니다. 유용한 메소드가 될 것 같아 기록해 보았습니다.


Written by@jaeyoung-son
배운것을 기록하는 공간입니다.

GitHub