목차
switch 문
switch 문은 여러 if 구문을 합친 것과 같다. switch 문은 일반적으로 조건이 특정 값인지 확인할 때 사용한다. 일반적으로 if 문을 자주 사용하지만, 조건이 여럿일 경우 switch 문을 사용하는 것이 효과적이다.
// 조건값에 따라 분기 실행
switch (조건) {
// 조건과 일치하는 값에 해당하는 코드 실행
case '값':
// ...
// break로 각 case 종료
break
// 어떤 case와도 일치하지 않을 때 실행되는 블록
// 선택 사항(없어도 문법 오류 없음)
default:
// ...
}
break 키워드를 생략하면 여러 상황을 구분해 동일한 작업을 수행할 수 있다. 수행해야 할 코드가 많은 경우 case 절을 중괄호({})로 묶을 수 있다.
switch(취미) {
case '수영':
case '스킨스쿠버':
case '윈드서핑': {
// ...
break
}
case '영화 감상': {
// ...
break
}
// ...
}
폼(Form) 핸들링
폼은 사용자가 정보를 입력할 수 있도록 하는 네이티브 컴포넌트로, 웹 애플리케이션에 필수적이다. 사용자가 폼을 제출(submit)하면 입력한 정보는 클라이언트에서 처리되거나, 서버로 전송된다.
폼 구성
모든 폼은 <form> 요소로 구성되며 폼 요소의 대부분은 다음의 속성을 가진다.
- method: 폼 제출에 사용되는 방법(메서드)으로 GET, POST와 같은 HTTP 메서드(기본 값: GET)
- action: 제출할 정보를 전달받을 서버 URL(클라이언트에서 처리할 때 설정 필요 없음)
폼에는 최소한 하나의 필드(field)와 submit 타입의 버튼이 필요하다. 폼 필드에는 다음 세 가지가 필요하다.
- label: 폼 필드 레이블(이름). 사용자가 접근하는 필드에 대해 이해하고 사용할 수 있으므로 필수
- id: 폼 필드 레이블의 for 속성에 연결될 입력 필드 식별자
- name: 서버에 전송할 데이터 이름. 서버에 필드 값({ email: ‘...’ })을 추출할 수 있다.
submit 타입 버튼이나 input 요소를 사용하면 사용자가 폼을 제출할 수 있다.
<form action="/server-url">
<div class="form-field">
<label for="usermail">이메일</label>
<input id="usermail" name="email" type="email" />
</div>
<button type="submit">보내기</button>
</form>
폼 제출
사용자는 다음의 두 가지 방법으로 폼을 제출한다.
- submit 버튼 클릭
- Enter 키 입력
사용자가 폼을 제출할 때 submit 이벤트가 발생하므로 이벤트 리스너를 통해 submit 이벤트를 수신(listening)할 수 있다. 기본적으로 폼은 action 속성에 설정된 서버 URL에 제출되지만, 클라이언트에서 처리할 때는 기본 작동을 방지하고 제어한다.
const form = document.querySelector('.form')
form.addEventListener('submit', (e) => {
e.preventDefault()
console.log('폼 제출')
})
클라이언트에서 제어
폼의 기본 작동을 방지하고 클라이언트에서 제어하는 데에는 두 가지 이유가 있다.
- 서버 제출 전 처리: 입력된 폼 정보를 서버에 전송하기 전에 JavaScript를 사용해 필드 값을 변경하거나 새 필드를 만들 수 있다. 폼을 제출할 준비가 되면 form.submit() 메서드를 호출해 프로세스를 이어갈 수 있다.
- 비동기 처리: 입력된 폼 정보를 서버에 제출하지 않고, 비동기(AJAX) 방식을 사용해서 JavaScript로 서버와 데이터를 주고 받을 수 있다.
JavaScript로 폼 필드 선택
JavaScript로 폼 필드를 선택하는 방법은 두 가지다. .querySelector() 메서드를 사용하거나 form.elements를 이용할 수 있다.
모든 폼 요소는 .elements 속성을 가지는데 이 속성은 세 가지 유형의 속성-값 쌍을 포함하는 객체를 반환한다.
- 폼 필드의 index
- 폼 필드의 name
- 폼 필드의 id
for ... of 문을 사용해 form.elements의 각 폼 필드에 접근할 수 있다. 또는, form.elements를 배열로 변환하고 .forEach() 메서드를 사용해 접근할 수도 있다.
// for ... of 문
for (const formField of form.elements) {
console.log(formField)
}
// forEach() 메서드
Array.from(form.elements).forEach((formField) => {
console.log(formField)
})
특정 폼 필드를 선택하려면 name이나 id, 또는 index를 사용할 수 있다.
// 인덱스(index) 예시
const firstFormField = form.elements[0]
// 네임(name) 예시
const input1 = form.elements.input1
// 아이디(id) 예시
const input2 = form.elements['input-two']
폼 필드 이벤트
웹 사이트나 웹 애플리케이션을 구축할 때 일반적으로 다음 세 가지 타입의 폼 필드를 사용한다.
- <input />
- <textarea>
- <select>
<input />
입력 필드는 위 세 가지 타입 중 가장 많은 기능을 가지고, 다양한 정보를 수집하는 데 사용한다. 여러 브라우저에서 지원되는 입력 필드 타입은 다음과 같다.
- 텍스트(text, 기본 값)
- 이메일(email)
- 숫자(number)
- 비밀번호(password)
- 전화번호(tel)
- 검색(search)
- 사이트 주소(url)
사용자가 입력한 입력 필드의 값을 가져올 때는 .value 속성을 사용한다. 입력 값 좌우에 불필요한 공백이 있을 수 있으니 .trim() 메서드를 사용하여 좌우 공백을 제거하는 것이 좋다.
const usernameInput = document.querySelector('input[name="username"]')
console.log(usernameInput.value) // ' 이름 '
console.log(usernameInput.value.trim()) // '이름'
입력 필드는 다음과 같은 4가지 타입의 이벤트를 수신할 수 있다.
- change: 사용자가 입력 중인 필드를 떠날 때 이벤트 발생
- input: 사용자가 입력 필드에 입력할 때마다 이벤트 발생
- focus 또는 focusin: 사용자가 입력 필드를 탭하거나 클릭하여 활성화할 때마다 이벤트 발생. focus 이벤트는 버블링이 안 되고 focusin 이벤트는 버블링이 된다.
- blur 또는 focusout: 사용자 입력 필드가 초점을 잃을 때마다 이벤트 발생. blur 이벤트는 버블링이 안 되고, focusout 이벤트는 버블링이 된다.
체크박스
<input /> 요소에 type=”checkbox” 속성을 지정하면 체크박스를 만들 수 있다. 체크박스에는 label이 필요하며, 레이블이 없으면 아무런 의미를 가지지 않는다. 여러 체크박스를 사용할 때는 체크박스마다 name 속성을 다르게 지정해야 한다.
<form class="form">
<p>어떤 과일을 좋아하세요?</p>
<div class="checkbox">
<input type="checkbox" name="apple" id="apple" />
<label for="apple">🍎 사과</label>
</div>
<div class="checkbox">
<input type="checkbox" name="grape" id="grape" value="포도" />
<label for="grape">🍇 포도</label>
</div>
<div class="checkbox">
<input type="checkbox" name="lemon" id="lemon" checked />
<label for="lemon">🍋 레몬</label>
</div>
</form>
체크박스의 값(value)은 .value 속성으로 가져올 수 있다. .value 속성 값이 설정되지 않았을 때는 "on" 값을 반환한다.
const form = document.querySelector('.form')
const appleCheckbox = form.elements.apple
const grapeCheckbox = form.elements.grape
console.log(appleCheckbox.value) // 'on'
console.log(grapeCheckbox.value) // '포도'
체크박스의 체크(checked) 상태는 .checked 속성으로 확인할 수 있다.
const form = document.querySelector('.form')
const appleCheckbox = form.elements.apple
const grapeCheckbox = form.elements.grape
const lemonCheckbox = form.elements.lemon
console.log(appleCheckbox.checked) // false
console.log(grapeCheckbox.checked) // false
console.log(lemonCheckbox.checked) // true
체크박스는 change와 input 이벤트를 수신할 수 있다. 체크박스 상태가 전환될 때마다 이벤트가 발생한다. 각 체크박스에 이벤트를 연결하는 대신 이벤트 위임을 사용하면 편리하다.
form.addEventListener('change', (e) => {
const checkbox = e.target.closest('input[type="checkbox"]')
if (checkbox) console.log(checkbox.checked)
})
라디오 버튼
라디오 버튼은 여러 옵션 중 하나만 체크해야 할 때 사용한다. 라디오 버튼을 만들기 위해서는 type 속성을 radio로 지정하면 된다. 라디오 버튼에도 label이 필요하고, 레이블이 없으면 아무런 의미를 가지지 않는다.
서로 관계가 있는 여러 라디오 버튼의 name 속성은 동일한 값을 가져야 한다. 서버에 값을 전달하려면 value 속성이 필요하다.
<form class="form">
<p>어떤 과일을 좋아하세요? (하나만 체크 가능)</p>
<div class="radio-button">
<input type="radio" name="favorite-fruit" id="apple" value="사과" />
<label for="apple">🍎 사과</label>
</div>
<div class="radio-button">
<input type="radio" name="favorite-fruit" id="grape" value="포도" />
<label for="grape">🍇 포도</label>
</div>
<div class="radio-button">
<input type="radio" name="favorite-fruit" id="lemon" value="레몬" />
<label for="lemon">🍋 레몬</label>
</div>
<div class="radio-button">
<input type="radio" name="favorite-fruit" id="none" value="없음" />
<label for="none">😝 없음</label>
</div>
</form>
라디오 버튼은 체크 박스와 비슷하게 값(value)은 .value 속성으로 가져오고, 체크 상태는 .checked 속성으로 확인 할 수 있고, change와 input 이벤트를 수신할 수 있다. 라디오 버튼의 상태가 전환될 때마다 이벤트가 발생한다. 따라서, 라디오 버튼도 각 라디오 버튼에 이벤트를 연결하는 대신, 이벤트 위임을 사용할 수 있다.
form.addEventListener('change', (e) => {
const radioButton = e.target.closest('input[type="radio"]')
if (radioButton) console.log(radioButton.checked)
})
<textarea>
텍스트 에어리어(Textarea)는 여러 줄 텍스트 정보를 입력받을 때 사용한다.
<div class="textarea">
<label for="comment">댓글</label>
<textarea
id="comment"
name="comment"
placeholder="댓글을 작성해주세요."
cols="60"
rows="3"
></textarea>
</div>
<textarea> 요소에 입력된 값을 가져올 때는 .value 속성을 사용하고, 좌우 공백을 제거하기 위해 .trim() 메서드를 사용할 수 있다.
const comment = document.getElementById('comment')
console.log(comment.value.trim())
<textarea> 요소로 수신할 수 있는 이벤트 타입은 다음과 같다.
- change
- input
- focus / focusin
- blur / focusout
XSS 공격 & 살균(Sanitize)
XSS
크로스 사이트 스크립팅(XSS)은 해킹에 사용되는 방법 중 하나다. 해킹을 조심하지 않을 경우, 해커에 의해 웹 사이트가 공격받을 수 있다. 이 공격은 해커가 웹 사이트에서 JavaScript를 실행하도록 유도한다. 이와 같은 방법으로 JavaScript가 실행되면 심각한 피해가 발생할 수 있다. 예를 들어 사용자의 이름과 비밀번호를 갈취 당하는 경우가 발생한다.
이 공격을 방지하려면 다른 사람이 웹 사이트에서 JavaScript를 실행하지 못하도록 차단해야 한다. JavaScript 실행을 방지하는 가장 기본적인 방법은 출력 결과를 안전하게 처리하는 것이다.
XSS 방지
웹 사이트에 입력된 내용을 깨끗하게 만들면 문제를 예방할 수 있다. 입력된 내용을 깨끗하게 하는 방법은 두 가지다.
- textContent 또는 가능하면 value 사용: HTML을 생성하지 않고 오직 텍스트만 출력하기 때문에 JavaScript를 실행할 수 없다.
- 살균제(Sanitizer) 사용: 오픈 소스인 dompurify - npm을 사용하면 입력된 내용을 살균해 깨끗하게 만들 수 있다.
dompurify
DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. It's written in JavaScript and works in all modern browsers (Safari, Opera (15+), Internet Explorer (10+), Firefox and Chrome - as well as almost anything else usin.
www.npmjs.com
<!-- 살균제(Sanitizer) dompurify 사용 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.6/purify.min.js"></script>
// 입력된 HTML 코드를 살균하려면 DOMPurify.sanitize() 메서드 사용
form.addEventListener('submit', (e) => {
// ...
output.innerHTML = DOMPurify.sanitize(value)
})
오늘 하루를 돌아보며
폼으로 입력받은 내용을 어떻게 처리하고, 어떤 작업을 할 수 있는지 배웠다. HTML/CSS를 배울 때도 느꼈지만, <form> 요소로 할 수 있는 기능이 정말 다양하다. 기본적으로 가지고 있는 능력도 많은데, JavaScript를 사용하면 더 다양해지는 게 신기하다.
알면 알수록 많은 기능이 숨어있고, 신기한 기능이 있다. 심지어 지금도 계속 업데이트되는 중이라니! 그만큼 끊임없이 배워야 하고, 새로운 기능이 나오는지 눈여겨봐야 한다. 하나씩 차근차근 배워나가다 보면 mdn 페이지를 스르륵 보기만 해도 이해할 수 있는 날이 오겠지? 오늘도 화이팅!