새로운 프로젝트에 참여하게 되면서, 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가 필요합니다
// onChange로 관리할 문자열
const [hashtag, setHashtag] = useState<string | ''>('')
// 해시태그를 담을 배열
const [hashArr, setHashArr] = useState<string[] | []>([])
미리 말씀 드리자면, 제가 구현해봤을 때 div 안에 input만 존재하는 구조로는 dom 이벤트를 동적으로 제거하기 힘들었습니다. 따라서 저는 div 내부에 div와 input의 구조로 작업하였습니다
<div className="HashWrap" css={hashDivrap}>
<div className="HashWrapOuter"></div>
<input
className="HashInput"
type="text"
value={hashtag}
onChange={onChangeHashtag}
onKeyUp={onKeyUp}
placeholder="해시태그 입력"
/>
</div>
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;
}
`
setHashArr((hashArr) => [...hashArr, hashtag])
setHashtag('')
DOM 요소의 조작을 통해 useState([ ]) 배열의 state를 동적으로 조작할 수 있었습니다!
MDN Array.prototype.filter
MDN spread operator
키보드 키코드
velog, useState를 사용해서 배열에 값 추가하기