ckazubyqigyqb515__1080_790 과제: https://codestates.notion.site/5f83f7a007664f1abcf0cdbcbbbbd521

🚀배포

배포: https://friendly-fermi-7483ff.netlify.app/

GitHub : https://github.com/NamgyungKim/wanted_pre_onboarding

👩‍💻구현

📌 Toggle

  • 토글버튼 클릭 시 toggleBtn 상태가 bool 값으로 나타남


1 Toggle

✔ 구현방법

  • checkBox의 체크상태를 useState를 사용해 변경해주었다.

    // checkbox 체크여부
    const [toggleBtn, setToggleBtn] = useState(false);
    
  • Toggle 클릭에 따른 ON/OFF 변경

    1. Toggle를 클릭하면 체크박스가 선택/선택취소 가 되고, ref를 통해 받아온 체크박스의 상태가 setToggleBtn(toggleCheck.current.checked)로 변경된다.

      return (
        //input checkbox
        const toggleCheck = useRef();
        {/* Toggle */}
        <ToggleBtn onClick={() => setToggleBtn(toggleCheck.current.checked)}>
          <input id="toggle" type="checkbox" ref={toggleCheck} />
          <label htmlFor="toggle">
            <span />
          </label>
        </ToggleBtn>
      );
      
    2. 스타일은 css 에서 checkbox가 선택되었을때를 :checked 를 통해 변경해 주었다. 이때 배경에 움직이는 파란색을 span으로 두고 좌우로 이동시켜주었다. 동그라미원또한 우측으로 58px 이동시켜주었다.

      input {
        display: none;
        transition: all 0.4s;
        :checked + label span {
          transform: translateX(0);
        }
        :checked + label:before {
          transform: translateX(58px);
        }
      }
      span {
        display: block;
        position: absolute;
        left: 0;
        width: 100px;
        height: 40px;
        transform: translateX(-100%);
        background-color: var(--main-color);
        transition: all 0.4s;
        content: "";
      }
      

✔ 어려웠던 점

보통 토글버튼을 만들때 background색만 바꿔주는 식으로 많이들 한다.
하지만 요구사항에서는 background색을 바꾸지 않고 색이있는 박스가 좌우로 슬라이드 되는 형식으로 구현되어있었다.
그부분에대해서 어떻게 구현할까 생각을 하다가 label의 overflow를 hidden으로 두고, <span />을 label안에 넣어 translateX()를 이용하여 좌우로 움직여주었다.





📌 Modal

  • Open Modal 버튼 클릭시 모달 창이 열리고, 배경이 rgba(0, 0, 0, 0.3) 색으로 바뀐다.
  • x 버튼 클릭 시 모달창이 닫힌다.

2 Modal

✔ 구현방법

  • useState의 true/false값을 통해 모달창의 보여줌 여부를 결정하였다.

    const [showModal, setShowModal] = useState(false);
    
  • 모달창의 열리고 닫힘

    1. ModalBtn을 클릭 시 onClick에서 showModal을 true로 변경
    2. 이후 모달창의 x클릭시에 showModal을 false로 변경
    3. showModal에 따라서 ModalWindow가 나타나고 사라진다.

      return(
        /* 모달버튼 */
        <ModalBtn type="button" onClick={() => setShowModal(true)}>
          Open Modal
        </ModalBtn>;
        /* 모달창 */
        {showModal ? (
          <ModalWindow>
            <div>
              <button onClick={() => setShowModal(false)} />
              <p>HELLOW CODESTATES</p>
            </div>
          </ModalWindow>
        ) : null}
      )
      





📌 Tab

  • 탭버튼 클릭 시 해당 탭내용이 보여진다.

3 Tab

✔ 구현방법

  • Tab매뉴는 map을 통해서 생성했다. 그래서 tabMenu를 추가할 경우에도 자동으로 Tab메뉴의 길이가 늘어난다.

    // Tab 컨텐츠
    const Tab1 = () => <div>Tab menu One</div>;
    const Tab2 = () => <div>Tab menu Two</div>;
    const Tab3 = () => <div>Tab menu Three</div>;
    // Tab 매뉴명, 컨텐츠
    const tabMenu = [
      { name: "Tab1", con: <Tab1 /> },
      { name: "Tab2", con: <Tab2 /> },
      { name: "Tab3", con: <Tab3 /> },
    ];
    cosnt Tab = () => {
      return (
        <>
          <TabBox>
          {
            tabMenu.map((item, num) => (
            <div
              className={tabBtn === num ? "active" : ""}
              onClick={() => setTebBtn(num)}
              key={num}
            >
              {item.name}
            </div>
          ))}
          </TabBox>
          <Content>{tabMenu[tabBtn].con}</Content>
        </>
      )
    }
    
  • 탭 버튼 클릭 시 해당 탭의 내용이 보인다.

    1. 탭 글릭 시 tabMenu의 index번호로 useState가 변경된다.
    2. <Content>{tabMenu[tabBtn].con}</Content>를 통해 index번호의 컨텐츠가 나타난다.





📌 Tag

  • input에 텍스트를 입력하고 엔터를 했을 때 Tag 생성
  • 생성된 Tag는 오른쪽의 x 버튼을 통해 제거 가능

4 Tag

✔ 구현방법

  • usState에 배열을 추가

    1. input에 택스트를 입력하고 엔터를 누를 시 addTag()함수 실행
    2. tags에 input텍스트의 value를 배열뒤에 추가 시키고 value를 지워준다.

      const [tags, setTags] = useState(["CodeState", "codding"]);
      const inputRef = useRef(); //input 안의 value를 가져오기위해 사용했다.
      // 태그 추가
      const addTag = (e) => {
        if (e.key === "Enter") {
          //tags내용을 item으로 복사 해서 사용
          const item = tags.slice();
          item.push(inputRef.current.value);
          setTags(item);
          inputRef.current.value = "";
        }
      };
      return (
        <TagBox>
          {/* tags */}
          {tags.map((item, num) => {
            return (
              <TagBtn key={num}>
                <span>{item}</span>
                <button onClick={() => removeTag(num)} />
              </TagBtn>
            );
          })}
          {/* input */}
          <Input
            ref={inputRef}
            onKeyPress={addTag}
            placeholder="Press enter to add tags"
            type="text"
          />
        </TagBox>
      );
      
  • 태그 제거

    1. 태그의 x 클릭 시 removeTag 함수실행, 클릭한 테그의 index값을 num으로 가져온다.
    2. 받아온num를 이용해 배열을 수정해준다.

      const [tags, setTags] = useState(["CodeState", "codding"]);
      // 태그 제거
      const removeTag = (num) => {
        //tags내용을 item으로 복사 해서 사용
        const item = tags.slice();
        item.splice(num, 1);
        setTags(item);
      };
      return (
        <TagBox>
          {/* tags */}
          {tags.map((item, num) => {
            return (
              <TagBtn key={num}>
                <span>{item}</span>
                <button onClick={() => removeTag(num)} />
              </TagBtn>
            );
          })}
        </TagBox>
      );
      

✔ 어려웠던 점

  • focus시에 input이 아닌 다른곳에 outline이 생성되도록하는건 이번에 처음 만들어봐서 다른 곳에서도 찾아보며 만들었다. css에 :focus-within이라는 속성이 있었고 그 속성만으로 간단하게 할 수 있었다.
  • 기존 input태그에는 :focus시 outline: none으로 해주고, 겉에 감싸는 box에 :focus-within 에 border값을 주었다.

    .TagBox {
      :focus-within {
        border: 1px solid var(--main-color);
      }
    }
    input {
      :focus {
        outline: none;
      }
    }
    





📌 AutoComplete

  • input에 택스트 입력 후 엔터시 입력결과가 저장이된다.
  • 택스트 입력 시 그 텍스트로 시작되는 이전 기록을 보여준다.
  • 가장최근에 입력했던 순으로 위에서부터 표시된다. 이전에 입력했던 값을 다시 입력했을 경우에도 상단으로 위치가 변경된다.
  • x 버튼 클릭시 입력되지 않고 텍스트만 제거된다.
  • 자동완성은 마우스로 클릭과 방향키로 선택 가능하다.
  • focus가 다른곳에 가있을때는 자동완성창이 닫힌다.

5 AutoComplete

✔ 구현방법

  • input입력시 자동완성 기능
    1. 자동완성할 키워드들 useState의 배열로 나열 앞에 있을수록 최근 입력값
    2. keyword arr를 input의 입력값에 따른 filter를 해준 후 map 으로 나타냄
      const [keyword, setKeyword] = useState([
        "abc",
        "apple",
        "ask",
        "admit",
        "acess",
        "test",
      ]);
      {
        showAutoComplete && inputValue !== "" ? (
       <ul ref={ulRef}>
         {keyword
           .filter((item) => {
             const regExp = new RegExp(`^${inputValue}`);
             return regExp.test(item);
           })
           .map((item, num) => {
             // 최대 10개만 보여주기
             if (num < 10) {
               return (
                 <li
                   onClick={focusAutoComplete}
                   onKeyDown={focusAutoComplete}
                   key={num}
                   tabIndex="0"
                   className="autoComplete"
                 >
                   {item}
                 </li>
               );
             }
             return null;
           })}
       </ul>
        ) : null;
      }
      
  • input 자동완성 선택 (클릭, 방향키)

    1. 자동완성된 키워드 클릭 시, 방향키로 focus시 focusAutoComplete 함수 실행
    let focusPosition = 0;
    const focusAutoComplete = (e) => {
      setShowAutoComplete(true);
      e.preventDefault(); //방향키 입력시 스크롤이동방지
      // 키워드 포커스 상태에서 엔터 하거나 키워드 클릭 시
      // input에 그 키워드 입력
      if (e.key === "Enter" || e.type === "click") {
        setInputValue(e.target.innerHTML);
      }
      // press up key
      // 만약 가장상단에서 up key 눌렸을 경우 배열 가장 아래가 focus
      if (e.keyCode === 38) {
        if (focusPosition === 0) {
          focusPosition = ulRef.current.children.length - 1;
          ulRef.current.children[0].focus();
        } else {
          focusPosition--;
        }
        // focusPosition을 포커스
        ulRef.current.children[focusPosition].focus();
        // 포커스된 value를 input에 넣어주기
        inputRef.current.value = ulRef.current.children[focusPosition].innerHTML;
      }
      // press down key
      // 만약 가장하단에서 down key 눌렸을 경우 배열 가장 위를 focus
      if (e.keyCode === 40) {
        if (focusPosition === ulRef.current.children.length - 1) {
          focusPosition = 0;
          ulRef.current.children[0].focus();
        } else {
          focusPosition++;
        }
        // focusPosition을 포커스
        ulRef.current.children[focusPosition].focus();
        // 포커스된 value를 input에 넣어주기
        inputRef.current.value = ulRef.current.children[focusPosition].innerHTML;
      }
    };
    
  • 자동완성 키워드 추가
    1. input에 text를 입력하고 엔터 시 addKeyword 함수 실행
      const addKeyword = (e) => {
        const copyKeyword = keyword.slice();
        // 2. 엔터키에서만 실행
        if (e.key === "Enter") {
       // 3. 엔터시 input의 값이 keyword에 이미 기존하는 값인지 판단
       if (keyword.indexOf(inputValue) === -1) {
         // 4-1. 배열에 없는 새로운 값이면 자동완성배열에 추가
         copyKeyword.unshift(inputValue);
         setKeyword(copyKeyword);
       } else {
         // 4-2. 기존하는 값이면 배열 가장 앞으로 이동
         copyKeyword.splice(copyKeyword.indexOf(inputValue), 1);
         setKeyword(copyKeyword);
         copyKeyword.unshift(inputValue);
       }
       // 5. input의 value 지우고, 포커스아웃
       setInputValue("");
       setShowAutoComplete(false);
        }
      };
      

✔ 어려웠던 점

  • 이번 과제에서 가장 어려웠던 부분인였다.
    우선 완료후에도 시현해보며 생각과 다르게 작동하는 부분이 많아서 수정해주느라 오래걸렸다.
    input안에서 방향키를 눌렀을경우에도 자동완성에 focus되도록 하고, 방향키 입력시 스크롤이동방지를 해주는등 자잘하게 수정해주어야할 것들이 많았다.
    지금은 어찌어찌 콘솔에 뜨는 애러창도 다 해결을 했고, 코드도 나름 정리를 했으나 다른 더 좋은 방식이 있지 않을까 하는 생각이 든다.





📌 ClickToEdit

  • 입력 상자 클릭 시 input으로 변경되며 값을 입력 할 수 있게된다.
  • 입력이 끝난 후 focus out을 하면 아래 결과가 변경된다.

6 ClickToEdit

✔ 구현방법

  • 입력 택스트(text)와 변경중 여부(chainge)를 useStage로 두었다.

    const [name, setName] = useState({
      text: "김남경",
      chainge: false,
    });
    
  • 클릭 시 input box로 변경되고 focusOut시 div박스로 변경

    1. div박스를 클릭 시 useState로 chainge: false > true로 변경
    2. name.chainge가 true일 경우, false일경우 보여지는 html변경

      const inputName = useRef();
      return (
        <>
          {name.chainge ? (
            <div>
              <label>이름</label>
              <input
                ref={inputName}
                name="name"
                onBlur={nameOnBlur}
                type="text"
              />
            </div>
          ) : (
            <div>
              <span>이름</span>
              <div onClick={() => setName({ ...name, chainge: true })}>
                {name.text}
              </div>
            </div>
          )}
        </>
      );
      
  • div > input 로 변경시 input에 바로 focus

    1. div박스를 클릭 시 useState로 chainge: false > true로 변경경
    2. useState의 name.chainge가 병경되며 useEffect안의 if 문 실행
    3. if 문 name.chainge가 true일 경우, inputName.current가 있을 경우 실행
    4. inputName.current.focus();로 input에 포커스.
      useEffect(() => {
        if (name.chainge && inputName.current) {
          inputName.current.focus();
          inputName.current.value = name.text;
        }
      }, [name]);
      
  • inputbox에서 focusOut을 했을 때 값 변경

    1. focusOut 했을경우 nameOnBlur함수 실행
    2. inpu의 value를 e.target으로 가져와서 Sate변경
      const nameOnBlur = (e) =>
        setName({ ...name, text: e.target.value, chainge: false });
      

✔ 어려웠던 점

  • 입렧상자를 클릭 했을때 input으로 바뀌며 동시에 바로 input에 포커스 되도록 해야하는 부분에서 어려움을 겪었다. 처음에는 div에 onClick 함수 안에inputName.current.focus()를 넣어주었다. 하지만 그렇게 했을 경우 아직 inputbox가 랜더링 되기 전이라 오류가 떴다.
    그래서 이부분을 useEffect를 이용해서 해결해주었다.
    useEffect로 name의 변경을 감지하고, 이후 input에대한 focus() 처리를 해주니 잘 동작했다. © 2022 GitHub, Inc. Terms Privacy Security Status Docs Contact GitHub Pricing API Training Blog About





Leave a comment