[혼공스] 6주차: 문서 객체 모델, 예외 처리
본문 바로가기

혼공 스터디/혼자 공부하는 자바스크립트

[혼공스] 6주차: 문서 객체 모델, 예외 처리

728x90
반응형

 

 

Chap 7 문서 객체 모델

1. 문서 객체 조작하기

  • HTML 페이지에 있는 html, head, body, title, h1, div, span 등을 HTML 언어에서는 요소라고 부름
  • 자바스크립트에서는 이를 문서 객체라고 부른다
  • 따라서 문서 객체를 조작한다는 말을 HTML 요소들을 조작한다는 의미
  • 문서 객체 모델 (DOM, Document Objects Model)
    • 문서 객체를 조합해서 만든 전체적인 형태
    • 제이쿼리와 같은 라이브러리와 리액트와 같은 프레임워크를 사용하기 때문에 조작이 쉬움

 

1) DOMContentLoaded 이벤트

  • 문서 객체를 조작할 때 사용하는 이벤트
  • 오탈자를 입력해도 오류를 발생하지 않으니 절대 틀리지말고 입력하길!
HTML 코드를 자바스크립트로 조작하기
<!DOCTYPE html>
<html>
  <head>
    <title>DOMContentLoaded</title>
    <script>
      // HTML 태그를 쉽게 만들 수 있는 콜백 함수를 선언합니다.
      const h1 = (text) => `<h1>${text}</h1>`
    </script>
    <script>
      document.body.innerHTML += h1('1번째 script 태그')
    </script>
  </head>
  <body>
    <script>
      document.body.innerHTML += h1('2번째 script 태그')
    </script>
    <h1>1번째 h1 태그</h1>
    <script>
      document.body.innerHTML += h1('3번째 script 태그')
    </script>
    <h1>2번째 h2 태그</h1>
  </body>
</html>
  • body 태그가 생성되기 이전에 head 태그 안의 script 태그에서 body 태그를 조작하던 부분
    (<h1>1번째 script 태드</h1>를 출력하는 부분)은 화면에 출력되지 않음
  • 기본적으로 head 태그 내부에 script 태그를 배치하면 body 태그에 있는 문서 객체(요소)에 접근할 수 없음
  • head 태그 내부의 script 태그에서 body 태그에 있는 문서에 접근하려면 화면에 문서 객체(요소)를
    모두 읽어들일 때까지 기다려야 함
  • DOMContentLoaded 이벤트는 웹 브라우저가 문서 객체를 모두 읽고 나서 실행하는 이벤트

 

DOMContentLoaded 이벤트
<!DOCTYPE html>
<html>
  <head>
    <title>DOMContentLoaded</title>
    <script>
      // DOMContentLoaded 이벤트를 연결합니다.
      document.addEventListener('DOMContentLoaded', () => {
        const h1 = (text) => `<h1>${text}</h1>`
        document.body.innerHTML += h1('DOMContentLoaded 이벤트 발생')
      })
    </script>
  </head>
  <body>

  </body>
</html>

 

2) 문서 객체 가져오기

  • document.body 코드를 사용하면 body 요소를 읽어들일 수 있다
  • 이외에도 HTML 문서에 있는 head 요소와 title 요소 등은 다음과 같은 방법으로 읽어들일 수 있다.

  • 우리가 head 요소와 body 요소 내부에 만든 다른 요소들은 다음과 같은 별도의 메소드를 사용해서 접근

  • 선택자 부분에는 CSS 선택자를 입력
  • CSS 선택자는 매우 다양
  • querySelector()
  • querySelectorAll()
querySelector() 메소드
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 요소를 읽어들입니다.,
        const header = document.querySelector('h1')

        // 텍스트와 스타일을 변경합니다.
        header.textContent = 'HEADERS'
        header.style.color = 'white'
        header.style.backgroundColor = 'black'
        header.style.padding = '10px'
      })
    </script>
  </head>
  <body>
    <h1></h1>
  </body>
</html>
  • querySelector() :: h1 태그를 추출하고 조작하는데 사용됨
querySelectorAll() 메소드
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 요소를 읽어들입니다.
        const headers = document.querySelectorAll('h1')

        // 텍스트와 스타일을 변경합니다.
        headers.forEach((header) => {
          header.textContent = 'HEADERS'
          header.style.color = 'white'
          header.style.backgroundColor = 'black'
          header.style.padding = '10px'
        })
      })
    </script>
  </head>
  <body>
    <h1></h1>
    <h1></h1>
    <h1></h1>
    <h1></h1>
  </body>
</html>
  • querySelectorAll() :: 문서 객체 여러 개를 배열로 읽어들이는 함수
  • 따라서 내부의 요소에 접근하고 활용하려면 반복을 돌려야함
  • 일반적으로 forEach() 메소드를 사용해서 반복을 돌림

 

3) 글자 조작하기

  • 지금까지 살펴본 예제들에서 innerHTML 속성, textContent 속성을 사용해서 문서 객체 내부의 글자를 조작
속성 이름 설명
문서 객체.textContent 입력된 문자열을 그대로 넣습니다.
문서 객체.innerHTML 입력된 문자열을 HTML 형식으로 넣습니다
  • textContent 속성은 입력된 문자열을 그대로 넣어줌
  • innerHTML 속성은 입력된 문자열을 HTML 형식으로 넣어줌
글자 조작하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const a = document.querySelector('#a')
        const b = document.querySelector('#b')

        a.textContent = '<h1>textContent 속성</h1>'
        b.innerHTML = '<h1>innerHTML 속성</h1>'
      })
    </script>
  </head>
  <body>
    <div id="a"></div>
    <div id="b"></div>
  </body>
</html>
  • textContent 속성은 글자가 그대로 들어갔지만, innerHTML 속성은 <h1>~</h1>을 h1 요소로 변환해서 들어갔음

4) 속성 조작하기

메소드 이름 설명
문서 객체.setAttribute(속성이름, 값) 특성 속성에 값을 지정한다.
문서 객체.getAttribute(속성이름) 특정 속성을 추출합니다.
속성 조작하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const rects = document.querySelectorAll('.rect')

        rects.forEach((rect, index) => {
          const width = (index + 1) * 100
          const src = `http://placekitten.com/${width}/250`
          rect.setAttribute('src', src)
        })
      })
    </script>
  </head>
  <body>
    <img class="rect">
    <img class="rect">
    <img class="rect">
    <img class="rect">
  </body>
</html>
  • setAttribute()와 getAttribute()메소드를 사용하지 않고도 온점을 찍고 속성을 바로 읽어들이거나 지정 가능

 

5) 스타일 조작하기

  • 문서 객체의 스타일을 조작할 때는 style 속성을 사용
  • style 속성은 객체이며, 내부에는 속성으로 CSS를 사용해서 지정할 수 있는 스타일들이 있음
  • 이러한 속성에는 CSS로 입력할 때 사용하는 값과 같은 값을 입력
  • 자바스크립트에서는 - 기호를 식별자에 사용할 수 없으므로,
    두 단어 이상의 속성은 다음과 같이 캐멀 케이스로 나타냄
CSS 속성 이름 자바스크립트 style 속성 이름
background-color backgroundColor
text-align textAlign
font-size fontSize

스타일 조작하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const divs = document.querySelectorAll('body > div')

        divs.forEach((div, index) => {
          console.log(div, index)
          const val = index * 10
          div.style.height = `10px`
          div.style.backgroundColor = `rgba(${val}, ${val}, ${val})`
        })
      })
    </script>
  </head>
  <body>
    <!-- div 태그 25개 -->
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
    <div></div><div></div><div></div><div></div><div></div>
  </body>
</html>

6) 문서 객체 생성하기

  • 문서 객체를 생성하고 싶을 때에는 document.createElement() 메소드를 사용한다.

  • 그런데 문서 객체를 만들었다고 문서 객체가 배치되는 것은 아님
  • 문서를 어떤 문서 아래에 추가할지를 지정해줘야함
  • 이러한 그림을 프로그래밍에서 트리(tree)라고 부름
  • 어떤 문서 객체가 있을 때 위에 있는 것을 부모(parent)라고 부르고, 아래에 있는 것을 자식(child)라고 부름
  • 문서 객체에는 appendChild() 메소드가 있으며, 이를 활용하면 어떤 부모 객체 아래에 자식 객체를 추가할 수 있음
  • 문서 객체 트리 구조를 만드는 방법은 다음과 같음

문서 객체 생성하고 추가하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 문서 객체 생성하기
        const header = document.createElement('h1')

        // 생성한 태그 조작하기
        header.textContent = '문서 객체 동적으로 생성하기'
        header.setAttribute('data-custom', '사용자 정의 속성')
        header.style.color = 'white'
        header.style.backgroundColor = 'black'

        // h1 태그를 body 태그 아래에 추가하기
        document.body.appendChild(header)
      })
    </script>
  </head>
  <body>

  </body>
</html>

7) 문서 객체 이동하기

  • appendChild() 메소드는 문서 객체를 이동할 때도 사용할 수 있음
  • 문서 객체의 부모는 언제나 하나여야 한다.
  • 따라서 문서 객체를 다른 문서 객체에 추가하면 문서 객체가 이동
문서 객체 이동하기 
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 문서 객체 읽어들이고 생성하기
        const divA = document.querySelector('#first')
        const divB = document.querySelector('#second')
        const h1 = document.createElement('h1')
        h1.textContent = '이동하는 h1 태그'

        // 서로 번갈아가면서 실행하는 함수를 구현합니다.
        const toFirst = () => {
          divA.appendChild(h1)
          setTimeout(toSecond, 1000)
        }
        const toSecond = () => {
          divB.appendChild(h1)
          setTimeout(toFirst, 10000)
        }
        toFirst()
      })
    </script>
  </head>
  <body>
    <div id="first">
      <h1>첫 번째 div 태그 내부</h1>
    </div>
    <hr>
    <div id="second">
      <h1>두 번째 div 태그 내부</h1>
    </div>
  </body>
</html>

 

8) 문서 객체 제거하기

  • 문서 객체를 제거할 때는 removeChild() 메소드를 사용

  • appendChild() 메소드 등을 부모 객체와 이미 연결이 완료된 문서 객체의 경우 parentNode 속성으로 부모 객체에 접근
  • 일반적으로 어떤 문서 객체를 제거할 떄는 다음과 같은 형태의 코드를 사용

문서 객체 제거하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        setTimeout(() => {
          const h1 = document.querySelector('h1')

          h1.parantNode.removeChild(h1)
          // document.body.removeChild(h1)
        }, 3000)
      })
    </script>
  </head>
  <body>
    <hr>
    <h1>제거 대상 문서 객체</h1>
    <hr>
  </body>
</html>

 

9) 이벤트 설정하기

  • 모든 문서 객체는 생성되거나 클릭되거나 마우스를 위에 올리거나 할 때 이벤트라는 것이 발생
  • 이 이벤트가 발생할 때 실행할 함수는 addEventListener() 메소드를 사용

  • 이벤트가 발생할 때 실행할 함수를 이벤트 리스너(event listener) 또는 이벤트 핸드러(event handler)라고 부름
이벤트 연결하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        let counter = 0
        const h1 = document.querySelector('h1')

        h1.addEventListener('click', (event) => {
          counter++
          h1.textContent = `클릭 횟수:${counter}`
        })
      })
    </script>
    <style>
      h1{
        /* 클릭을 여러 번 했을 때
           글자가 선택되는 것을 막기 위한 스타일 */
        user-select: none;
      }
    </style>
  </head>
  <body>
    <h1>클릭 횟수: 0</h1>
  </body>
</html>
  • 이벤트를 제거할 때는 removeEventListener() 메소드를 활용

  • 이벤트 리스너 부분에는 연결할 때 사용했던 이벤트 리스너를 넣는다.
  • 변수 또는 상수로 이벤트 리스너를 미리 만들고, 이를 이벤트 연결과 연결 제거에 활용
이벤트 연결 제거하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        let counter = 0
        let isConnect = false

        const h1 = document.querySelector('h1')
        const p = document.querySelector('p')
        const connectButton = document.querySelector('#connect')
        const disconnectButton = document.querySelector('#disconnect')

        const listener = (event) => {
          h1.textContent = `클릭 횟수: ${counter++}`
        }

        connectButton.addEventListener('click', () => {
          if (isConnect === false) {
            h1.addEventListener('click', listener)
            p.textContent = '이벤트 연결 상태: 연결'
            isConnect = true
          }
        })
        disconnectButton.addEventListener('click', () => {
          if (isConnect === true) {
            h1.removeEventListener('click', listener)
            p.textContent = '이벤트 연결 상태: 해제'
          }
        })
      })
    </script>
    <style>
      h1{
        /* 클릭을 여러 번 했을 때
           글자가 선택되는 것을 막기 위한 스타일 */
        user-select: none;
      }
    </style>
  </head>
  <body>
    <h1>클릭 횟수: 0</h1>
    <button id="connect">이벤트 연결</button>
    <button id="disconnect">이벤트 제거</button>
    <p>이벤트 연결 상태: 해제</p>
  </body>
</html>

 

2.이벤트 활용

  • 입력 양식(form)에 관련된 내용을 많이 사용
  • 입력 양식에 관련된 내용을 알기 위해서는 사전에 HTML 기본에 대한 공부가 많이 필요

1) 이벤트 모델

  • 이벤트를 연결하는 방법을 이벤트 모델이라고 부름
  • 이벤트를 연결할 때 addEventListener() 메소드를 사용 → 표준 이벤트 모델

표준 이벤트 모델

 

  • 고전 이벤트 모델 :: on○○으로 시작하는 속성에 함수를 할당해서 이벤트를 연결하는 방법

고전 이벤트 모델

 

  • 인라인 이벤트 모델 :: 고전 이벤트 모델처럼 onOO으로 시작하는 속성을
    HTML 요소에 직접 넣어서 이벤트를 연결하는 것

인라인 이벤트 모델

  • 인라인 이벤트 모델은 HTML 요소의 onOO 속성에 자바스크립트 코드를 넣는 것
  • 현재 코드에서는 listener()라는 함수를 호출하고 있음
  • 이때 onOO 속성 내부에서 변수 event를 활용할 수 있음
  • 이 변수를 listener() 함수의 매개변수로 전달
  • 모든 이벤트 모델의 이벤트 리스너는 첫 번째 매개변수로 이벤트 객체를 받는다.

 

2) 키보드 이벤트

이벤트 설명
keydown 키가 눌릴 때 실행. 키보드를 꾹 누르고 있을 때도,
입력될 때도 실행됨
keypress 키가 입력되었을 때 실행
웹브라우저에 따라서 아시아권의 문자(한중일)를
제대로 처리하지 못하는 문제가 있음
keyup 키보드에서 키가 떨어질 때 실행
  • keydown 이벤트와 keypress 이벤트는 웹 브라우저에 따라서 아시아권의 문자를 제대로 처리하지 못하는 문제가 있어
    일반적으로는 keyup 이벤트를 사용함.
남은 글자 수 출력하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const textarea = document.querySelector('textarea')
        const h1 = document.querySelector('h1')

        textarea.addEventListener('keyup', (event) => {
          const length = textarea.value.length
          h1.textContent = `글자 수: ${length}`
        })
      })
    </script>
  </head>
  <body>
    <h1></h1>
    <textarea></textarea>
  </body>
</html>
  • keypress 이벤트로 구현하면 아시아권의 문자는 공백이 들어가기 전까지는 글자 수를 세지 않는다.
  • 아예 keypress 이벤트가 발생하지 않는다.
  • keyup 이벤트는 키가 키보드에서 떨어질 때 발생
    → 특정 키를 꾹 누르고 있으면 글자 수를 세지 않는다.

(1) 키보드 키 코드 사용하기

  • 키보드 이벤트가 발생할 때는 이벤트 객체로 어떤 키를 눌렀는지와 관련된 속성들이 따라옴
이벤트 속성 이름 설명
code 입력한 키
keyCode 입력한 키를 나타내는 숫자
altKey Alt 키를 눌렀는지
ctrlKey Ctrl 키를 눌렀는지
shiftKey Shift 키를 눌렀는지
  • code 속성은 입력한 키를 나타내는 문자열이 들어있고, altKey, ctrlKey, shiftKey 속성은 해당 키를 눌렀는지 불 자료형 값이 들어있음
키보드 이벤트와 관련된 이벤트 속성
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const h1 = document.querySelector('h1')
        const print = (event) => {
          let output = ''
          output += `alt: ${event.altKey}<br>`
          output += `ctrl: ${event.ctrlKey}<br>`
          output += `shift: ${event.shiftKey}<br>`
          output += `code: ${typeof(event.code) !== 'undefined' ?
            event.code : event.keyCode}<br>`
        h1.innerHTML = output
        }

        document.addEventListener('keydown', print)
        document.addEventListener('keyup', print)
      })
    </script>
  </head>
  <body>
    <h1></h1>
  </body>
</html>
키로 별 움직이기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 별의 초기 설정
        const star = document.querySelector('h1')
        star.style.position = 'absolute'

        // 별의 이동을 출력하는 기능
        let [x, y] = [0, 0]
        const block = 20
        const print = () => {
          star.style.left = `${x * block}px`
          star.style.top = `${y * block}px`
        }
        print()

        // 별을 이동하는 기능
        const [left, up, right, down] = [37, 38, 39, 40]
        document.body.addEventListener('keydown', (event) => {
          switch (event.keyCode) {
            case left:
              x -= 1
              break
            case up:
              y == 1
              break
            case right:
              x += 1
              break
            case down:
              y += 1
              break
          }
          print()
        })
      })
    </script>
  </head>
  <body>
    <h1></h1>
  </body>
</html>

3) 이벤트 발생 객체

  • 코드의 규모가 커지면 이처럼 이벤트 리스너를 외부로 분리하는 경우가 많아짐
  • 해결 방법
    1. event.currentTarget 속성을 사용
      • () => {} 와 function () {} 형태 모두 사용이 가능
    2. this 키워드 사용
      • 화살표 함수가 아닌 function () {} 형태로 함수를 선언한 경우에 사용
    3.  

4) 글자 입력 양식 이벤트

  • 사용자로 부터 어떠한 입력을 받을 때 사용하는 요소를 입력 양식(form)이라고 부름.
  • HTML에서는 input 태그, textarea 태그, button 태그, select 태그 등이 모두 입력 양식
입력 양식을 기반으로 inch를 cm 단위로 변환하는 프로그램
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const input = document.querySelector('input')
        const button = document.querySelector('button')
        const p = document.querySelector('p')

        button.addEventListener('click', () => {
          // 입력을 숫자로 변환합니다.
          const inch = Number(input.value)
          // 숫자가 아니라면 바로 리턴합니다.
          if (isNaN(inch)) {
            p.textContent = '숫자를 입력해주세요'
            return
          }
          // 변환해서 출력합니다.
          const cm = inch * 2.54
          p.textContent = `${cm} cm`
        })
      })
    </script>
  </head>
  <body>
    <input type="text"> inch<br>
    <button>계산</button>
    <p></p>
  </body>
</html>
  • isNaN() 함수의 결과가 true로 나오는 숫자가 아닌 경우 바로 return 키워드로 리턴해서 이후의 코드를 실행하지 않음
  • 위와 같이 사용하면 들여쓰기 단계를 하나 줄일 수 있으므로 코드가 깔끔해짐
이메일 형식 확인하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const input = document.querySelector('input')
        const p = document.querySelector('p')
        const isEmail = (value) => {
          // 골뱅이를 갖고 있고 && 골뱅이 뒤에 점이 있다면
          return (value.indexOf('@') > 1)
            && (value.split('@')[1].indexOf('.') > 1)
        }

        input.addEventListener('keyup', (event) => {
          const value = event.currentTarget.value
          if (isEmail(value)) {
            p.style.color = 'green'
            p.textContent = `이메일 형식입니다: ${value}`
          } else {
            p.style.color = 'red'
            p.textContent = `이메일 형식이 아닙니다: ${value}`
          }
        })
      })
    </script>
  </head>
  <body>
    <input type="text">
    <p></p>
  </body>
</html>
  • ismail() :: indexOf() 함수 등을 활용해서 매개변수로 전달된 값이 이메일인지 확인하고 true 또는 false를 리턴함.

(1) 드롭다운 목록 활용하기

  • select 태그로 구현
기본 select 태그
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const select = document.querySelector('select')
        const p = document.querySelector('p')

        select.addEventListener('change', (event) => {
          const options = event.currentTarget.options
          const index = event.currentTarget.options.selectedIndex

          p.textContent = `선택: ${options[index].textContent}`
        })
      })
    </script>
  </head>
  <body>
    <select>
      <option>떡볶이</option>
      <option>순대</option>
      <option>오뎅</option>
      <option>튀김</option>
    </select>
    <p>선택: 떡볶이</p>
  </body>
</html>
  • 코드를 실행하고 드롭다운 목록에서 항목을 선택하면 options[index]에서 선택한 option 태그가 출력
  • 현재 코드에서는 textContent 속성을 바로 추출해서 사용했는데, option 태그에 다른 속성들을 부여하고 속성을 활용할 수도 있음
  • selcet 태그에 multiple 속성을 부여하면 Ctrl 또는 Shift 키를 누르고 여러 항목 선택할 수 있는 상자가 나옴
multiple select 태그
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const select = document.querySelector('select')
        const p = document.querySelector('p')

        select.addEventListener('change', (event) => {
          const options = event.currentTarget.options
          const list = []
          for (const option of options) {
            if (option.selected) {
              list.push(option.textContent)
            }
          }
          p.textContent = `선택: ${list.join(',')}`
        })
      })
    </script>
  </head>
  <body>
    <select multiple>
      <option>떡볶이</option>
      <option>순대</option>
      <option>오뎅</option>
      <option>튀김</option>
    </select>
    <p></p>
  </body>
</html>
cm 단위를 여러 단위로 변환하는 프로그램
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        let 현재값
        let 변환상수 = 10

        const select = document.querySelector('select')
        const input = document.querySelector('input')
        const span = document.querySelector('span')

        const calculate = () => {
          span.textContent = (현재값 * 변환상수).toFixed(2)
        }

        select.addEventListener('change', (event) => {
          const options = event.currentTarget.options
          const index = event.currentTarget.options.selectedIndex
          변환상수 = Number(options[index].value)
          calculate()
        })

        input.addEventListener('keyup', (event) => {
          현재값 = Number(event.currentTarget.value)
          calculate()
        })
      })
    </script>
  </head>
  <body>
    <input type="text"> cm =
    <span></span>
    <select>
      <option value="10">mm</option>
      <option value="0.01">m</option>
      <option value="0.393701">inch</option>
    </select>
  </body>
</html>

(2) 체크 박스 활용하기

체크 박스 확용하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        let [timer, timerId] = [0, 0]
        const h1 = document.querySelector('h1')
        const checkbox = document.querySelector('input')

        checkbox.addEventListener('change', (event) => {
          if (event.currentTarget.checked) {
            // 체크 상태
            timerId = setInterval(() => {
              timer += 1
              h1.textContent = `${timer}초`
            }, 1000)
          } else {
            // 체크 해제 상태
            clearInterval(timerId)
          }
        })
      })
    </script>
  </head>
  <body>
    <input type="checkbox">
    <span>타이머 활성화</span>
    <h1></h1>  
  </body>
</html>

(3) 라디오 버튼 활용하기

라디오 버튼 활용하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 문서 객체 추출하기
        const output = document.querySelector('#output')
        const radios = document.querySelectorAll('[name=pet]')

        // 모든 라디오 버튼에
        radios.forEach((radio) => {
          // 이벤트 연결
          radio.addEventListener('change', (event) => {
            const current = event.currentTarget
            if (current.checked) {
              output.textContent = `좋아하는 애완동물은 ${current.value}이시군요!`
            }
          })
        })      
      })
    </script>
  </head>
  <body>
    <h3># 좋아하는 애완동물을 선택해주세요</h3>
    <input type="radio" name="pet" value="강아지">
    <span>강아지</span>
    <input type="radio" name="pet" value="고양이">
    <span>고양이</span>
    <input type="radio" name="pet" value="햄스터">
    <span>햄스터</span>
    <input type="radio" name="pet" value="기타">
    <span>기타</span>
    <hr>
    <h3 id="output"></h3>
  </body>
</html>

 

 

5) 기본 이벤트 막기

  • 웹 브라우저는 이미지에서 마우스 오른쪽 버튼을 클릭하면 다음과 같은 컨텍스트 메뉴를 출력
  • 어떤 이벤트가 발생했을 때 웹 브라우저가 기본적으로 처리해주는 것을 기본 이벤트라고 함
  • 기본 이벤트를 제거할 때는 event 객체의 preventDefault() 메소드를 사용한다.
이미지 마우스 오른쪽 버튼 클릭 막기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const imgs = document.querySelectorAll('img')
       
        imgs.forEach((img) => {
          img.addEventListener('contextmenu', (event) => {
            event.preventDefault()
          })
        })
      })
    </script>
  </head>
  <body>
    <img src="http://placekitten.com/300/300" alt="">
  </body>
</html>
인터넷에서 이미지 불펌 방지 등을 구현할 때 사용하는 코
체크 때만 링크 활성화하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        let status = false
       
        const checkbox = document.querySelector('input')
        checkbox.addEventListener('change', (event) => {
          status = event.currentTarget.checked
        })

        const link = document.querySelector('a')
        link.addEventListener('click', (event) => {
          if (!status) {
            event.preventDefault()
          }
        })
      })
    </script>
  </head>
  <body>
    <input type="checkbox">
    <span>링크 활성화</span>
    <br>
    <a href="http://hanbit.co.kr">한빛미디어</a>
  </body>
</html>

 

 

 

6) 할 일 목록 만들기 [선택 미션]

할 일 목록 만들기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
    <h1>할 일 목록</h1>
    <input id="todo">
    <button id="add-button">추가하기</button>
    <div id="todo-list">


    </div>
  </body>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      // 문서 객체를 가져옵니다.
      const input = document.querySelector('#todo')
      const todoList = document.querySelector('#todo-list')
      const addButton = document.querySelector('#add-button')

      // 변수를 선언합니다.
      let keyCount = 0

      // 함수를 선언합니다.
      const addTodo = () => {
        // 입력 양식에 내용이 없으면 추가하지 않습니다.
        if (input.value.trim() === '') {
          alert('할 일을 입력해주세요.')
          return
        }

        // 문서 객체를 설정합니다.
        const item = document.createElement('div')
        const checkbox = document.createElement('input')
        const text = document.createElement('span')
        const button = document.createElement('button')

        // 문서 객체를 식별할 키를 생성합니다.
        const key = keyCount
        keyCount += 1

        // item 객체를 조작하고 추가합니다.
        item.setAttribute('data-key', key)
        item.appendChild(checkbox)
        item.appendChild(text)
        item.appendChild(button)
        todoList.appendChild(item)

        // checkbox 객체를 조작합니다.
        checkbox.type = 'checkbox'
        checkbox.addEventListener('change', (event) => {
          item.style.textDecoration
            = event.target.checked ? 'line-through' : ''
        })

        // text 객체를 조작합니다.
        text.textContent = input.value

        // button 객체를 조작합니다.
        button.textContent = '제거하기'
        button.addEventListener('click', () => {
          removeTodo(key)
        })

        // 입력 양식의 내용을 비웁니다.
        input.value = ''
      }

      const removeTodo = (key) => {
        // 식별 키로 문서 객체를 제거합니다.
        const item = document.querySelector(`[data-key="${key}"]`)
        todoList.removeChild(item)
      }

      // 이벤트 연결
      addButton.addEventListener('click', addTodo)
      input.addEventListener('keyup', (event) => {
        // 입력 양식에서 Enter 키를 누르면 바로 addTodo() 함수를 호출합니다.
        const ENTER = 13
        if (event.keyCode === ENTER) {
          addTodo()
        }
      })
    })
  </script>
</html>

7) 타이머로 구현한 남은 글자 수 세기

  • 트위트는 다음과 같이 타이머를 사용해서 50밀리초마다 입력 양식 내부의 글자를 확인해서 글자 수를 센다.
  • focus 이벤트와 blur 이벤트를 사용
글자 수 출력하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const textarea = document.querySelector('textarea')
        const h1 = document.querySelector('h1')
        let timerId

        textarea.addEventListener('focus', (event) => {
          timerId = setInterval(() => {
            const length = textarea.value.length
            h1.textContent = `글자 수: ${length}`
          }, 50)
        })
        textarea.addEventListener('blur', (event) => {
          clearInterval(timerId)
        })
      })
    </script>
  </head>
  <body>
    <h1></h1>
    <textarea></textarea>
  </body>
</html>

8) localStorage 객체

  • 웹 브라우저가 기본적으로 제공하는 객체
  • localStorage.getltem(키): 저장된 값을 추출하고 없으면 undefined가 나옴.
    객체의 속성을 추출하는 일반적인 형태로 localStorage.키 또는 localStorage[키] 형태로 사용
  • localStorage.setltem(키, 값): 값을 저장
    이전과 마찬가지로 객체에 속성을 지정하는 일반적인 형태를 사용할 수도 있음
  • localStorage.removeltem(키): 특정 키의 값을 제거
  • localStorage.clear(): 저장된 모든 값을 제거
웹 브라우저에 데이터를 저장하는 localStorage 객체와 활용하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      document.addEventListener('DOMContentLoaded', () => {
        const p = document.querySelector('p')
        const input = document.querySelector('input')
        const button = document.querySelector('button')

        const savedValue = localStorage.getItem('input')
        // localStorage.input도 가능합니다.
        if (savedValue) {
          input.value = savedValue
          p.textContent = `이전 실행 때의 마지막 값: ${savedValue}`
        }

        input.addEventListener('keyup', (event) => {
          const value = event.currentTarget.value
          localStorage.setItem('input', value)
          // localStorage.input = value도 가능합니다.
        })

        button.addEventListener('click', (event) => {
          localStorage.clear()
          input.value = ''
        })
      })
    </script>
  </head>
  <body>
    <p></p>
    <button>지우기</button>
    <input type="text">
  </body>
</html>
  • localStorage처럼 웹 브라우저가 제공해주는 기능을 웹 API라고 부름
  • 책으로도 출판이 별로 안 되어 있는 내용이므로 다음 모질라 문서에서 어떤 API가 있는지 확인해보고
    구글 검색 등으로 어떻게 사용하는지 찾아서 공부해야 하는 부분임.

 

 

Chap 8 예외 처리

1. 구문 오류와 예외

  • 구문 오류(syntex error)
    • 괄호 개수를 잘못 입력하는 등의 오류 코드가 실행조차 되지 않는 오류
  • 예외(excetion)
    • 문법적 오류를 제외하고 코드 실행 중간에 발생하는 오류
  • 예외 처리(exception handling)
    • 이를 처리하는 것

1) 오류의 종류

  • 구문 오류 :: 프로그램 실행 전에 발생하는 오류
  • 예외 / 런타임 오류 :: 프로그램 실행 중에 발생하는 오류

(1) 구문 오류

  • 괄호의 짝을 맞추지 않았다든지, 문자열을 열었는데 닫지 않았다든지 할 때 발생하는 오류
  • 이러한 구문 오류가 있으면 웹 브라우저가 코드를 분석조차 하지 못하므로 실행 되지 않는다.

(2) 예외, 런타임 오류

  • 실행 중에 발생하는 오류
  • SyntaxError라고 출력되는 오류 이외의 모든 오류(TypeError, ReferenceError, RangeError)

2) 기본 예외 처리

  • 조건문을 사용해서 예외가 발생하지 않게 만드는 것
querySelector() 메소드로 추출된 문서 객체가 없는 경우
<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
 
  </body>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const h1 = document.querySelector('h1')
      h1.textContent = '안녕하세요'
    })
  </script>
</html>
Uncaught TypeError TypeError: Cannot set properties of null (setting 'textContent') at <anonymous> (c:\예제\chap8\8-1-1.html:12:22)
기본 예외 처리
<!DOCTYPE html>
<html>
  <head>
    <title></title>
  </head>
  <body>
 
  </body>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const h1 = document.querySelector('h1')
      if (h1) {
        h1.textContent = '안녕하세요'
      } else {
        console.log('h1 태그를 추출할 수 없습니다.')
      }
    })
  </script>
</html>
h1 태그를 추출할 수 없습니다.

3) 고급 예외 처리

  • try catch finally 구문을 사용하는 방법
  • try catch finally 구문의 기본적인 형태

  • try 구문 안에서 예외를 발생하면 이를 catch 구문에서 처리함
  • finally 구문은 필수 사항은 아니며 예외 발생 여부와 상관없이 수행해야 하는 작업이 있을 때 사용한다.
  • 다음 코드는 변수 willExcept 자체가 존재하지 않는다, willExcept의 byeBye() 메소드를 사용함
  • willExcept 객체도 없고 byeBye() 메소드도 존재하지 않는다.

  • try 구문 안에서 예외가 발생하면 더 이상 try 구문을 진행하지 않고 catch  구문을 실행한다
try catch 구문의 사용
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      try {
        willExcept.byeBye()
        console.log("try 구문의 마지막 줄")
      } catch (exception) {
        console.log("catch 구문의 마지막 줄")
      }
    </script>
  </head>
  <body>
  </body>
</html>
catch 구문의 마지막 줄
finally 구문
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      try {
        willExcept.byeBye()
        console.log("try 구문의 마지막 줄")
      } catch (exception) {
        console.log("catch 구문의 마지막 줄")
      } finally {
        console.log("finally 구문의 마지막 줄")
      }
    </script>
  </head>
  <body>
  </body>
</html>
catch 구문의 마지막 줄
finally 구문의 마지막 줄

4) finally 구문을 사용하는 이유

예외 처리 구문 내부에서 return 사용하기(1)
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      function test() {
        try {
          alert('A 위치입니다.')
          throw "예외 강제 발생"
        } catch (exception) {
          alert('B 위치입니다.')
          return
        }
        alert('C 위치입니다.')
      }

      // 함수를 호출합니다.
      test()
    </script>
  </head>
  <body>
  </body>
</html>
예외 처리 구문 내부에서 return 사용하기(2)
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      function test() {
        try {
          alert('A 위치입니다.')
          throw "예외 강제 발생"
        } catch (exception) {
          alert('B 위치입니다.')
          return
        } finally {
          alert('C 위치입니다.')
        }
      }

      // 함수를 호출합니다.
      test()
    </script>
  </head>
  <body>
  </body>
</html>
  • 예제(1)은 return 키워드를 사용해 함수를 벗어났으므로 "C위치 입니다"를 출력하지 않는다.
  • 예제(2)는 3가지 모두 출력 => finally 구문을 반드시 실행한다.
  • 이처럼 다음과 같은 경우에 결과가 달라짐
    • try catch 구문 내부에서 return 키워드를 만날 때
    • try catch 구문 내부에서 break 또는 continue 키워드를 만날 때

2. 예외 처리 고급

  • 예외 객체(exception object)
    • 예외가 발생하면 예외와 발생된 정보를 확인할 수 있는 것
    • 개발자가 예외를 강제로 발생시켜줘야 하는 경우가 많다.
    • 예외를 강제로 발생시킬 때 throw 키워드를 사

1) 예외 객체 

  • try catch 구문을 사용할 때 catch의 괄호 안에 입력하는 식별자가 예외 객체
  • 아무 식별자나 입력해도 괜찮지만, 일반적으로 e나 exception이라는 식별자를 사용!

속성 이름 설명
name 예외 이름
message 예외 메지지
예외 정보 출력하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      try {
        const array = new Array(999999999999999)
      } catch (exception) {
        console.log(exception)
        console.log()
        console.log(`예외 이름: ${exception.name}`)
        console.log(`예외 메시지: ${exception.message}`)
      }
    </script>
  </head>
  <body>
  </body>
</html>

2) 예외 강제 발생

  • 상황에 따라서 예외를 강제로 발생시켜야 하는 경우도 있음
  • 예외를 강제로 발생시킬 때는 throw 키워드를 사용
  • throw 구문은 다음과 같은 형태로 사용

예외 강제로 발생시키고 잡기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      function divide(a, b) {
        if (b === 0) {
          throw '0으로는 나눌 수 없습니다.'
        }
        return a / b
      }

      console.log(divide(10, 2))
      console.log(divide(10, 0))
    </script>
  </head>
  <body>
  </body>
</html>
예외를 강제로 발생시키기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      function test(object) {
        console.log(object.a + object.b)
      }

      test({})
    </script>
  </head>
  <body>
  </body>
</html>
NaN
  • 일반적인 프로그래밍 언어라면,
    • Object 객체에 a 속성과 b 속성이 없으므로 예외를 발생할 것이고,
    • 존재하지 않는 것을 더하므로 object.a + object.b를 할 때도 예외를 발생
  • 그러면 사용자는 자신이 잘못 사용했다는 것을 인지하고 수정할 수 있음

 

  • 하지만 자바스크립트는,
    • object.a가 undefined로 나오며, object.b도 undefined로 나옴
    • 여기에서 undefined + undefined를 하면 NaN이 나옴
  • 즉, 아무 오류 없이 코드가 정상적으로 실행
  • 자바스크립트는 undefined와 NaN이라는 값이 있어서 다른 프로그래밍 언어에 비해서 예외를 적게 발생시
  • 사용자에게 함수를 잘못 사용했다는 것을 강제로라도 인지시켜줄 필요가 있다.
예외를 강제로 발생시켜서 사용 유도하기
<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
      function test(object) {
        if (object.a !== undefined && object.b !== undefined) {
          console.log(object.a + object.b)
        } else {
          throw new Error("a 속성과 b 속성을 지정하지 않았습니다.")
        }
      }

      test({})
    </script>
  </head>
  <body>
  </body>
</html>

728x90
반응형