[React] velog, 해시태그 만들기!

이준희·2021년 9월 1일
14

REACT 정복기

목록 보기
4/9
post-custom-banner

무슨 태그를 말하는 건데?

새로운 프로젝트에 참여하게 되면서, form 양식에 글을 쓴 이후 해시태그를 포함한 데이터를 서버로 보내는 작업을 담당하게 되었습니다

자바스크립트 deep dive를 공부하면서 DOM 에 대한 공부를 하다보니 자신감이 어느정도 붙은 것 같습니다..!

따라서 이번에는 input 이벤트를 바탕으로 동적으로 태그를 추가하고, 이를 useState로 관리해보는 시간을 가졌습니다

계획하기

프로젝트 작업에 앞서, 피그마로 작성된 문서를 바탕으로 요구사항을 먼저 계획해보았습니다.

  • 처음에는 리액트에서 제공하는 useRef 훅함수를 사용하여 각 state 별 관리를 하고 싶었지만, useRef로 관리되는 함수를 바탕으로 '전송' 버튼을 활성화하는 부분 (주황색으로) 에서 적용이 불가능하여 다시 useState로 변경하였습니다.

  • 구글링 결과 form 태그 내부에는 중첩된 form 태그를 사용할 수 없었습니다

  • 따라서 input에 이벤트인 onKeyup 을 사용하여 enter key 코드를 받아 우리가 작성한 문자열을 DOM 요소의 innerHTML을 사용하여 동적으로 추가하는 방식을 사용하였습니다

분석하기

우선 코드를 바로 직접 만들기 전에, 벨로그의 콘솔창을 통해 데이터의 동작 과정을 확인해 보았습니다!

<div>
   <input 
		type="text"
		value={hashtag}
		onChange={onChangeHashtag}
		onKeyUp={onKeyUp}
		placeholder="해시태그 입력"
	/>
  
</div>

위와 같은 구조에서 <input> 태그에서 입력한 e.target.value를 onKeyUp 이벤트를 통해 받아 div 태그 내부에 동적으로 태그를 추가하는 것을 볼 수 있습니다!

따라서 form 데이터를 보낼 때 배열로 담을 ① state 와, onChange를 통해 배열 state에 저장할 문자열 ② state 총 2개의 state가 필요합니다

구현하기

① useState로 관리하기

// onChange로 관리할 문자열
 const [hashtag, setHashtag] = useState<string | ''>('')
  // 해시태그를 담을 배열
  const [hashArr, setHashArr] = useState<string[] | []>([])

미리 말씀 드리자면, 제가 구현해봤을 때 div 안에 input만 존재하는 구조로는 dom 이벤트를 동적으로 제거하기 힘들었습니다. 따라서 저는 div 내부에 div와 input의 구조로 작업하였습니다

② dom 구조 잡기

<div className="HashWrap" css={hashDivrap}>
  <div className="HashWrapOuter"></div>
  <input
    className="HashInput"
    type="text"
    value={hashtag}
    onChange={onChangeHashtag}
    onKeyUp={onKeyUp}
    placeholder="해시태그 입력"
  />
</div>
  • HashWrap : 전체를 담는 div
  • HashWrapOuter : 동적으로 생성되는 태그를 담을 div
  • HashInput : HashWrapOuter에 입력된 state를 추가할 input
  const onKeyUp = useCallback(
    (e) => {
      if (process.browser) {
        /* 요소 불러오기, 만들기*/
        const $HashWrapOuter = document.querySelector('.HashWrapOuter')
        const $HashWrapInner = document.createElement('div')
        $HashWrapInner.className = 'HashWrapInner'
        
        /* 태그를 클릭 이벤트 관련 로직 */
        $HashWrapInner.addEventListener('click', () => {
          $HashWrapOuter?.removeChild($HashWrapInner)
          console.log($HashWrapInner.innerHTML)
          setHashArr(hashArr.filter((hashtag) => hashtag))
        })

        /* enter 키 코드 :13 */
        if (e.keyCode === 13 && e.target.value.trim() !== '') {
          console.log('Enter Key 입력됨!', e.target.value)
          $HashWrapInner.innerHTML = '#' + e.target.value
          $HashWrapOuter?.appendChild($HashWrapInner)
          setHashArr((hashArr) => [...hashArr, hashtag])
          setHashtag('')
        }
      }
    },
    [hashtag, hashArr]
  )
  
 /* emotion css 태그 */ 
  
 const hashDivrap = css`
  margin-top: 24px;
  color: rgb(52, 58, 64);
  font-size: 1.125rem;
  display: flex;
  flex-wrap: wrap;
  letter-spacing: -0.6px;
  color: #444241;
  border-bottom: 1.6px solid ${Common.colors.gray04};
  padding: 2px 2px 8px 2px;

  .HashWrapOuter {
    display: flex;
    flex-wrap: wrap;
  }

  .HashWrapInner {
    margin-top: 5px;
    background: #ffeee7;
    border-radius: 56px;
    padding: 8px 12px;
    color: #ff6e35;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: bold;
    font-size: 1.4rem;
    line-height: 20px;
    margin-right: 5px;
    cursor: pointer;
  }

  .HashInput {
    width: auto;
    margin: 10px;
    display: inline-flex;
    outline: none;
    cursor: text;
    line-height: 2rem;
    margin-bottom: 0.75rem;
    min-width: 8rem;
    border: none;
  }
`

③ onKeyup 이벤트에 대한 함수 구현하기

⑴ 요소 만들기

  • onKeyup (키보드를 클릭했다가 해당 버튼에서 손을 뗄 때 발생하는 이벤트) input 태그를 통해 만들어진 해시 태그 div 들을 담을 HashWrapOuter를 선택합니다
  • $HashWrapInner 라는 상수를 만들어 새로운 div를 바인딩합니다
  • $HashWrapInner.className = 'HashWrapInner' 라는 작업을 통해 미리 적용해 놓은 css 파일을 적용합니다

⑵ enter key로 해시태그 동적으로 생성하기

  • enter key의 키 코드는 13입니다. 따라서 해당 키코드가 입력된(onKeyup)된 상황에서 해당 값(e.target.value)이 비어있지 않으면, $HashWrapInner의 내부 콘텐츠 노드에 innerHTML을 통해 해당 값을 바인딩합니다
  • $HashWrapInner는 아직 외부에 바인딩되지 않았으므로, $HashWrapOuter의 자식 노드로 $HashWrapInner을 추가합니다
setHashArr((hashArr) => [...hashArr, hashtag])
setHashtag('')
  • setHashArr은 HashArr useState를 통해 상태를 변화시킬 수있는 함수입니다.
  • ...(spread) 연산자를 통해 기존 값을 산개시킨 다음, 우리가 만든 hashtag를 뒤에 추가합니다
  • 이후 setHashtag 를 통해 hashtag의 값을 '' 빈 문자열로 만듭니다

⑶ 마우스 클릭으로 태그 제거하기

  • HashWrapInner (동적으로 생성된 해시태그 div)에 대해 클릭 이벤트가 발생하면 이에 따른 이벤트를 호출합니다
  • HashWrapOuter?.removeChild($HashWrapInner)를 통해 해당 함수를 HashWrapOuter의 자식 요소에서 제거합니다
  • 또한 setHashArr(hashArr.filter((hashtag) => hashtag)) 를 통해 기존 hashArr useState 배열에서 해당되는 hashtag를 제거한 배열값만 반환해줍니다

결과 확인하기

DOM 요소의 조작을 통해 useState([ ]) 배열의 state를 동적으로 조작할 수 있었습니다!

레퍼런스

MDN Array.prototype.filter
MDN spread operator
키보드 키코드
velog, useState를 사용해서 배열에 값 추가하기

profile
https://junheedot.tistory.com/ 이후 글 작성은 티스토리에서 보실 수 있습니다.
post-custom-banner

0개의 댓글