[프론트엔드] HTML 태그에 type 속성을 제대로 지정하지 않으면 큰일남
html태그의 type 속성을 정확하게 지정하는게 중요한 이유는 이벤트 충돌을 방지하고, 각 요소의 기본 동작을 제어하기 위함이다.
이렇게만 설명하면 크게 와닿지 않을 수 있는데, 최근에 이를 간과하고 개발을 하다가 잠시 당황했던 경험이 있어 공유해보고자 한다.
문제 상황: 두가지 이벤트가 한번에 발생
이 예제는 새로운 연락처 (이름, 전화번호, 그룹) 저장하고, 이 저장된 데이터를 기반으로 오른쪽에 촤르륵 띄워주는 간단한 사이트이다. 그리고 폼 안에 있는 '조직 추가' 버튼을 누르게 되면 아래와 같은 허접한 모달창이 나와 새 그룹 옵션을 추가해줄 수 있는 기능이 있다.
보기에는 정상적으로 작동할 것 같다. 하지만 의외로 그렇지 않은데, 폼을 제출하기 위해 엔터를 누르면 예상치 못한 일이 발생하게 된다.
일단 예상대로라면 form에서 정보를 입력하고 이를 제출했을때 바로 위 예시처럼 새로운 요소가 추가되는 이벤트만 발생해야 정상이다.
예상 로직
- 폼 제출 시: 입력 후 엔터를 누르면 폼 제출 이벤트가 발생한다.
- 조직 추가 버튼 클릭 시: 버튼 클릭 시 모달이 열리도록 설정.
하지만 문제는 폼 제출을 했을때 '조직 추가' 버튼을 눌렀을때만 떠야하는 모달창이 뜨고 폼 제출도 되는, 2가지의 이벤트가 동시에 발생한다는 것이다.
문제 상황과 분석
<form className="inputCon" onSubmit={saveContact}>
<InputField id="name" field="이름" value={name} setValue={setName} />
<InputField id="phone" field="전화번호" value={phone} setValue={setPhone} />
<SelectField
group={group}
setGroup={setGroup}
selectedGroup={selectedGroup}
setSelectedGroup={setSelectedGroup}
/>
<InputField id="record" field="간단한 기록" />
<button className="saveBtn" onClick={() => saveContact()}>
저장
</button>
</form>
<div className="selectField">
<label htmlFor="group" className="selectLabel">그룹</label>
<select
name="group"
id="group"
value={selectedGroup}
onChange={(e) => setSelectedGroup(e.target.value)}
>
{group.map((opt, i) => (
<option key={i} value={opt}>
{opt}
</option>
))}
</select>
<button onClick={addOption}>조직추가</button>
{isModalOpen && (
<GroupModal setModal={addOption} group={group} setGroup={setGroup} />
)}
</div>
문제의 흐름을 보면 다음과 같다.
1. 폼 내의 입력 필드에서 엔터를 누르면 폼이 제출되며 saveContact (새로운 연락처를 저장하는 로직)이 호출된다.
2. 이와 동시에 SelectField내에 있는 버튼의 addPost (모달창을 열고 새로운 옵션을 추가할 수 있는 로직)이 호출된다.
3. SelectField내에 있는 아래 예시의 버튼을 클릭해도 위와 같은 이벤트가 발생하게 된다, 즉 의도치않게 두 이벤트가 동시에 발생한다.
<button onClick={addOption}>조직추가</button>
결론부터 말하자면 문제의 원인은 이벤트전파였다.
그 이유는 버튼의 type을 따로 지정하지 않게 되면 submit으로 초기화되는데, 이렇게 초기화된 버튼이 폼 제출을 트리거하고, 버튼 클릭 이벤트가 함께 호출된 것이다.
해결 방법
이제 문제의 원인을 알았으니 아주 간단하게 해결이 가능하다.
<button type="button" onClick={addOption}>
조직추가
</button>
이렇게 문제가 발생한 버튼의 type 속성을 적절히 설정하면, 해당 버튼이 폼 제출과 무관하게 독립적으로 작동하도록 제어할 수 있다.
정리하자면, 역시 가장 중요한 건 기본기다.
- 이벤트 전파 제어: 클릭 이벤트와 폼 제출 이벤트를 분리하여 명확히 제어해야 함.
- type 속성 지정: 버튼의 type을 명확히 설정하여 불필요한 폼 제출을 방지.
- type="button": 폼 제출과 관계없는 버튼.
- type="submit": 폼 제출 버튼.
- 버튼에 type="button" 추가하여 폼 제출을 방지
- e.preventDefault() 사용하여 이벤트의 기본 동작 방지.