[TIL] React Custom Component 과제 Bare Minimum

Jade·2022년 10월 28일
1

Today I learn

목록 보기
42/77
post-thumbnail

😀 React Custom Component 과제

<Bare Minimum>

이번 과제는 App.js에서 불러오지 않는 UI 컴포넌트들을 구현했기 때문에, Storybook에서 UI들의 작동을 확인하면서 진행했다.


🟢 Modal

모달이라는 것은 기존 브라우저 페이지 위에 새로운 윈도우 창이 아니라 레이어를 까는 것을 말한다. 모달이 띄워진 경우 모달 창을 닫기 전까지는 그 아래의 페이지와는 상호작용이 불가능함.

팝업창은 현재 열린 페이지에서 또 다른 브라우저 페이지를 띄우는 것이기 때문에 둘은 비슷해보여도 다르다. 검색해보니 요즘은 팝업창을 잘 사용하지 않는다고 하는데, 새로운 창을 실행시키는 만큼 트래픽이 증가하며, 팝업창을 닫는 일이 번거로운 등 사용성을 해치기 때문이라고 한다.

아래에 적는 모든 코드들은 부분만 잘라 올린 것이기 때문에 불완전합니다.

1. state를 이용해 모달 창의 열고 닫힘 여부를 담을 수 있게 상태 변화 함수 작성하기.
: state는 비교적 최근에 했던 내용이라 상태 변화 함수를 작성하는 것까지는 어렵지 않았다. 다만 상태 변화 함수를 쓸 때는 좀 헷갈리긴 하는 것 같다. 갖고 있는 상태의 타입(?)이 다 다르기 때문에 조금 고민이 필요함.

const [isOpen, setIsOpen] = useState(false);

const openModalHandler = () => {
    setIsOpen(!isOpen); 
  // 처음에는 삼항 연산자를 사용했었는데 조금 더 간단하게 바꿨다 
  //isOpen === true ? setIsOpen(false) : setIsOpen(true)
  };


2. 위에서 만든 state를 이용해서 ModalView라는 모달 창 컴포넌트 렌더링 여부를 결정.

: 조건부 렌더링을 사용해서 isOpen이 true일 때만 ModalView를 포함한 요소들을 렌더링하고,
반대의 경우에는 null으로 작성하면 렌더링 되지 않는다.
조건부 렌더링 자체는 주로 삼항 연산자를 사용하기도 하고, 자바스크립트에서 조건문을 작성해주는 느낌이라 어렵진 않았는데, 요소들 사이에 중괄호를 끼워 넣어 그 안에 작성한다는 점이 여전히 낯설다. (이래도 되나 싶은...)

{isOpen ? (
          <ModalBackdrop ref={notCloseRef} onClick={openModalHandler}>
            <ModalView>
              <CloseBtn onClick={openModalHandler}>X</CloseBtn>
              <div className="desc">Hello CodeStates!</div>
            </ModalView>
          </ModalBackdrop>
        ) : null}

3. ModalBackdrop이라는 모달이 떴을 때 배경을 깔아주는 컴포넌트를 완성하고, 렌더링.
: ModalBackdrop이라는 컴포넌트는 div 요소로 화면 전체에 깔리도록 하고, 색을 반투명하게 조정해주는 작업이 필요했다.
또한 모달 백 드롭은 z-index를 사용해서 쌓이는 위치를 조정해줘야 했는데, z-index라는 속성을 이번 과제를 통해 처음 알게 되었다. MDN


4.모달창 외부나 닫힘 버튼을 클릭 했을 때는 모달이 닫히게 하되, 모달창 자체를 클릭했을 때는 닫히지 않게 하기.
: 이 부분이 제일 힘들었는데...결국 검색을 통해서 'stopPropagation'이라는 매서드가 있다는 것을 알게 되어서 해당 매서드를 사용해서 테스트를 통과했다.

  • useRef를 사용해서도 해결할 수 있었다!
//stopPropagation 이용
//ModalBackdrop에 onClick 이벤트로 인해 openModalHandler 함수가 실행될 때, 그 아래의 ModialView까지 퍼지지 않도록 해줌.
 <ModalBackdrop onClick={openModalHandler}>
            <ModalView onClick={(e) => e.stopPropagation()}>

//useRef 이용 
//주소값이 정확히 outerspace인 요소만 onClick시 OpenModalHandler 함수가 실행되도록 한다. (outerspace 주소값을 가진 ModalBackdrop 요소와 onClick 이벤트가 일어난 target 요소가 같을 때 핸들러 함수가 실행된다)
//OpenModalHandler 함수 대신 바로 setIsOpen(false)를 넣어줘도 되긴 함 
 const outerspace = useRef(null);

<ModalBackdrop
            ref={outerspace}
            onClick={(e) => {
              if (outerspace.current === e.target) {
                openModalHandler();
              }
            }}
          >




🟢 toggle

토글 과제은 모달 과제와 비슷한 점이 꽤 있었지만, 클래스를 추가하는 것에서 막혀서 한참 헤맸다.

과제 지시문에서 기본 CSS에서는 템플릿 리터럴과 삼항 연산자를 사용해 조건부 스타일링을 적용할 수 있다고 했지만, 똑같이 사용하면 안 되겠지...라고 생각해서 다른 방식으로 해야한다는 생각을 했기 때문이다. 😀🫠

결과적으로 className 속성의 값으로 조건부로 클래스를 덧붙여주는 작업을 할 수 있었다... (개발자들이 사용하기 편하라고 CSS in JS를 만들었다곤 했지만... 파일이 분리되어 있을 때보다 복잡한 느낌이 든다... 내가 잘 몰라서 그렇게 느끼는 걸수도 있지만 ㅎ)

<div className={`toggle-container ${isOn ? "toggle--checked" : null}`}/>
<div className={`toggle-circle ${isOn ? "toggle--checked" : null}`}/>

//toggle--checked는 토글의 상태인 isOn이 true일 때만 클래스로 부여되고, 그렇지 않은 경우에는 원래 div의 클래스인 toggle-container이나, toggle-circle만 부여된다. 




🟢 Tab

1. currentTab 상태와 상태 변화 함수인 setCurrentTab 작성.
: currentTab 상태는 현재 사용자가 클릭한 Tab의 인덱스를 갖고 있다.
인덱스는 단순한 값이기 때문에 그냥 setCurrentTab에 index만 넣어주면 되나...? 이게 맞나..? (무한 의심) 하면서 넣어봤는데 그게 맞았다.

  const [currentTab, setCurrentTab] = useState(0);

  const selectMenuHandler = (index) => {
    setCurrentTab(index);
  };

2. map을 통해서 menuArr라는 배열을 순회하면서 li 요소를 만들어주기.

3. 현재 내가 선택한 탭인 경우에는 원래 'submenu focused' 라는 클래스를 두 개를 지정해주고, 나머지 선택되지 않은 탭인 경우에는 'submenu'라는 클래스만 지정.

{menuArr.map((el, idx) => {
            return (
              <li
                className={idx === currentTab ? "submenu focused" : "submenu"}
                onClick={() => selectMenuHandler(idx)}
              >
                {el.name}
              </li>
            );
          })}




🟢 Tag

1.태그들의 배열 형태인 tags 상태에 새로운 태그를 추가하는 addTags 매서드 만들기.
: 단순하게 push로 배열에 추가하면 되는 거 아닌가... 생각하다 그게 강제로 값을 변화시키는 권장되지 않는 방법이라는 생각이 들어서 다른 방법을 고민했다.

게다가 이 addTags 매서드에는 입력된 태그가 이미 입력되었는지, 태그에 아무것도 입력되지 않은 상태로 enter가 눌린 것이 아닌지, enter로 입력되었는지 등등을 조건으로 걸어주어야 했고, 태그가 추가된 뒤 태그 입력창인 input 요소의 value를 빈 문자열로 바꿔주어야 했다.

우선 이벤트를 전달인자로 받았을 때, 해당 이벤트가 일어나는 객체를 'event.target'으로 불러올 수 있고, 해당 객체의 값이 'event.target.value'로 불러와지는데, 여기서 event.target.value를 하면 input에 입력된 값, 새로 입력된 태그의 값이 될 것이다.
길이가 꽤 길기 때문에 하나의 변수에 할당해주고, 그 값을 기준으로 조건문을 만들었다.

그리고 상태 변화 함수인 setTags 함수에 스프레드 연산자를 이용해 이미 담겨있던 tags 배열들의 요소들과, input 입력값을 넣어준 배열을 넣어줬다.

onst addTags = (event) => {
    let inputVal = event.target.value;
    if (
      inputVal.length !== 0 &&
      !tags.includes(inputVal) &&
      event.key === "Enter"
    ) {
      setTags([...tags, inputVal]);
      event.target.value = "";
    }

2. 태그를 삭제하는 메소드 완성하기.
: 배열에서 삭제하기를 slice로 해야하나 생각했다가 filter를 사용해 삭제해야 하는 태그를 제외한 나머지 태그들만 담긴 배열을 setTags에 담아주는 것이 낫겠다고 생각했다.

const removeTags = (indexToRemove) => {
    setTags(tags.filter((el, index) => index !== indexToRemove));
  };

//아래는 map으로 tags 배열을 순회하며 li를 만들어주고 있음.
//li 내부에 있는 span들 중 두번째 span이 x 버튼이 된다. 
<ul id="tags">
          {tags.map((tag, index) => (
            <li key={index} className="tag">
              <span className="tag-title">{tag}</span>
              <span
                className="tag-close-icon"
                onClick={() => removeTags(index)}
              >
                x
              </span>
            </li>
          ))}
        </ul>





🟡 CSS

CSS를 styled를 이용해서 작성하려고 하니 원래 CSS 파일에서는 알아서 자동완성 되었던 것이 작동하지 않아서 철자를 훨씬 더 잘 살펴가면서 해야했다. 스터디원 하나가 추천해준 익스텐션을 사용하면 styled를 이용해서도 VSCode에서 편리하게 작성이 가능하다고 한다~

익스텐션

새롭게 알게된 속성

  • z-index
  • 자식을 가리킬 때는 > 사용
  • hover, focus-within과 같은 효과 줄 때 는 & 사용
    		&:focus-within {
    border: 1px solid var(--coz-purple-600);
    }
    		```
profile
키보드로 그려내는 일

0개의 댓글