[TypeScript-03] 함수 타입과 DOM

Comely·2025년 3월 5일

TypeScript

목록 보기
3/9

함수 타입 정의

함수 타입 별칭 생성

// 함수 타입 정의
type NumOut = (x: number, y: number) => number

// 함수에 타입 적용
let ABC: NumOut = function(x, y) {
  return x + y
}

Arrow Function 기본 문법

// 일반 함수
function add(x, y) {
  return x + y
}

// Arrow Function
let add = (x, y) => {
  return x + y
}

// 단축 문법
let multiply = (x, y) => x * y           // return 생략
let double = x => x * 2                  // 매개변수 1개일 때 괄호 생략

Arrow Function 특징:

  • this 값이 일반 함수와 다름
  • 중괄호와 return 생략 가능 (단일 표현식일 때)
  • 매개변수 1개일 때 괄호 생략 가능

함수 타입 적용 방법

// Type Alias 사용
type CalculatorType = (x: number, y: number) => number
let calculator: CalculatorType = (a, b) => a + b

// 직접 타입 지정
let calculator: (x: number, y: number) => number = (a, b) => a + b

주의: function 키워드에는 직접 타입 지정 불가

// 불가능
function 함수이름: NumOut() {}

// 가능
let 함수이름: NumOut = function() {}

Object 내 함수 타입 지정

Object 메서드 정의

let 회원정보 = {
  name: 'kim',
  age: 30,
  plusOne(x) {
    return x + 1
  },
  changeName: () => {
    console.log('안녕')
  }
}

// 메서드 호출
회원정보.plusOne(1)
회원정보.changeName()

Object 메서드 타입 지정

type Member = {
  name: string,
  age: number,
  plusOne: (x: number) => number,
  changeName: () => void
}

let 회원정보: Member = {
  name: 'kim',
  age: 30,
  plusOne(x) {
    return x + 1
  },
  changeName: () => {
    console.log('안녕')
  }
}

고차 함수 (함수를 매개변수로 받는 함수)

콜백 함수 기본 개념

function 함수1(callback) {
  callback()  // 매개변수로 받은 함수 실행
}

function 함수2() {
  console.log('실행됨')
}

함수1(함수2)  // '실행됨' 출력

실전 예제: 문자열 처리 함수

// 문자열 앞의 0 제거
type CutType = (x: string) => string
let cutZero: CutType = function(x) {
  let result = x.replace(/^0+/, "")
  return result
}

// 대시 제거 후 숫자로 변환
type RemoveType = (x: string) => number
let removeDash: RemoveType = function(x) {
  let result = x.replace(/-/g, "")
  return parseFloat(result)
}

함수를 매개변수로 받는 함수

type CutType = (x: string) => string
type RemoveType = (x: string) => number

function 처리함수(
  text: string, 
  func1: CutType, 
  func2: RemoveType
) {
  let result1 = func1(text)    // 첫 번째 함수 실행
  let result2 = func2(result1) // 두 번째 함수 실행
  console.log(result2)
}

// 사용 예시
처리함수('010-1111-2222', cutZero, removeDash)  // 1011112222 출력

DOM 조작을 위한 설정

strictNullChecks 설정

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "strictNullChecks": true  // 또는 "strict": true
  }
}

HTML 준비

<!-- index.html -->
<h4 id="title">안녕하세요</h4>
<a id="link" href="naver.com">링크</a>
<button id="button">버튼</button>
<img id="image" src="test.jpg">

<script src="app.js"></script>

DOM 요소 타입과 Narrowing

querySelector의 null 문제

let 제목 = document.querySelector('#title')
제목.innerHTML = '반갑소'  // 에러! 제목이 null일 수 있음

문제 원인: querySelectorElement | null 타입 반환

해결방법 1: null 체크

let 제목 = document.querySelector('#title')
if (제목 != null) {
  제목.innerHTML = '반갑소'
}

해결방법 2: instanceof 사용 (권장)

let 제목 = document.querySelector('#title')
if (제목 instanceof HTMLElement) {
  제목.innerHTML = '반갑소'
}

해결방법 3: Type Assertion

let 제목 = document.querySelector('#title') as HTMLElement
제목.innerHTML = '반갑소'

해결방법 4: Optional Chaining

let 제목 = document.querySelector('#title')
if (제목?.innerHTML != undefined) {
  제목.innerHTML = '반갑소'
}

HTML 요소별 정확한 타입

주요 HTML 타입 계층구조

Element
├── HTMLElement
    ├── HTMLAnchorElement (a 태그)
    ├── HTMLImageElement (img 태그)
    ├── HTMLButtonElement (button 태그)
    ├── HTMLInputElement (input 태그)
    ├── HTMLHeadingElement (h1~h6 태그)
    └── ...

태그별 정확한 타입 지정

// a 태그의 href 속성 수정
let 링크 = document.querySelector('#link')
if (링크 instanceof HTMLAnchorElement) {
  링크.href = 'https://kakao.com'  // 정상 작동
}

// img 태그의 src 속성 수정
let 이미지 = document.querySelector('#image')
if (이미지 instanceof HTMLImageElement) {
  이미지.src = 'new.jpg'
}

// button 태그 이벤트 리스너
let 버튼 = document.querySelector('#button')
if (버튼 instanceof HTMLButtonElement) {
  버튼.addEventListener('click', function() {
    console.log('클릭됨')
  })
}

잘못된 타입 사용 예시

let 링크 = document.querySelector('#link')
if (링크 instanceof HTMLElement) {
  링크.href = 'https://kakao.com'  // 에러! HTMLElement에는 href 속성 없음
}

여러 요소 조작

querySelectorAll 사용

let 링크들 = document.querySelectorAll('.naver')

링크들.forEach((링크) => {
  if (링크 instanceof HTMLAnchorElement) {
    링크.href = 'https://kakao.com'
  }
})

for 반복문 사용

let 링크들 = document.querySelectorAll('.naver')

for (let i = 0; i < 링크들.length; i++) {
  let 링크 = 링크들[i]
  if (링크 instanceof HTMLAnchorElement) {
    링크.href = 'https://kakao.com'
  }
}

타입: querySelectorAllNodeListOf<Element> 반환


이벤트 리스너와 Optional Chaining

기본 이벤트 리스너

let 버튼 = document.getElementById('button')
if (버튼 instanceof HTMLButtonElement) {
  버튼.addEventListener('click', function() {
    console.log('안녕')
  })
}

Optional Chaining 활용

let 버튼 = document.getElementById('button')
버튼?.addEventListener('click', function() {
  console.log('안녕')
})

Optional Chaining 동작:

  • 버튼이 존재하면 addEventListener 실행
  • 버튼이 null이면 undefined 반환 (에러 없음)

실전 예제

예제 1: 이미지 변경 버튼

let 버튼 = document.querySelector('#changeBtn')
let 이미지 = document.querySelector('#image')

if (버튼 instanceof HTMLButtonElement && 이미지 instanceof HTMLImageElement) {
  버튼.addEventListener('click', function() {
    이미지.src = 'new.jpg'
  })
}

예제 2: 폼 데이터 처리

let= document.querySelector('#myForm')
let 입력창 = document.querySelector('#nameInput')

if (instanceof HTMLFormElement && 입력창 instanceof HTMLInputElement) {.addEventListener('submit', function(e) {
    e.preventDefault()
    console.log('입력값:', 입력창.value)
  })
}

예제 3: 다중 요소 스타일 변경

let 제목들 = document.querySelectorAll('h4')

제목들.forEach(제목 => {
  if (제목 instanceof HTMLHeadingElement) {
    제목.style.color = 'red'
    제목.style.fontSize = '20px'
  }
})

정규표현식 기본

문자열 처리 패턴

// 앞의 0 제거
let text = "00123"
let result = text.replace(/^0+/, "")  // "123"

// 모든 대시 제거
let phone = "010-1234-5678"
let cleaned = phone.replace(/-/g, "")  // "01012345678"

// 숫자만 추출
let mixed = "abc123def456"
let numbers = mixed.replace(/[^0-9]/g, "")  // "123456"

정규표현식 문법

  • /^0+/: 문자열 시작에서 0이 1개 이상
  • /-/g: 모든 대시 문자 (g는 global, 전체)
  • /[^0-9]/g: 숫자가 아닌 모든 문자

DOM 조작 모범 사례

1. 타입 가드 함수 생성

function isHTMLElement(element: Element | null): element is HTMLElement {
  return element instanceof HTMLElement
}

function isHTMLAnchorElement(element: Element | null): element is HTMLAnchorElement {
  return element instanceof HTMLAnchorElement
}

// 사용
let 링크 = document.querySelector('#link')
if (isHTMLAnchorElement(링크)) {
  링크.href = 'https://example.com'
}

2. 공통 DOM 조작 함수

function safeQuerySelector<T extends HTMLElement>(
  selector: string,
  type: new () => T
): T | null {
  let element = document.querySelector(selector)
  if (element instanceof type) {
    return element
  }
  return null
}

// 사용
let 버튼 = safeQuerySelector('#button', HTMLButtonElement)
if (버튼) {
  버튼.addEventListener('click', () => console.log('클릭'))
}

3. 에러 처리

function 안전한DOM조작() {
  try {
    let 요소 = document.querySelector('#target')
    if (요소 instanceof HTMLElement) {
      요소.innerHTML = '새 내용'
    } else {
      console.warn('요소를 찾을 수 없습니다')
    }
  } catch (error) {
    console.error('DOM 조작 중 오류:', error)
  }
}

실전 체크리스트

함수 타입

  • 함수 타입 별칭 정의
  • Arrow Function 문법 숙지
  • 콜백 함수 타입 지정
  • 고차 함수 구현

DOM 조작 준비

  • strictNullChecks 설정
  • HTML 파일 준비
  • TypeScript 컴파일 설정

타입 안전 DOM 조작

  • querySelector null 체크
  • 적절한 HTML 요소 타입 사용
  • instanceof로 타입 가드
  • Optional Chaining 활용

고급 기법

  • 여러 요소 일괄 처리
  • 이벤트 리스너 타입 안전
  • 정규표현식 문자열 처리
  • 공통 유틸리티 함수 작성

주의사항 및 팁

1. 타입 선택 가이드

// 일반적인 조작만 할 경우
if (element instanceof HTMLElement) { }

// 특정 속성 접근이 필요한 경우
if (element instanceof HTMLAnchorElement) { }  // href 접근
if (element instanceof HTMLImageElement) { }   // src 접근
if (element instanceof HTMLInputElement) { }   // value 접근

2. 성능 고려사항

  • getElementById는 더 빠르고 HTMLElement 반환
  • querySelector는 유연하지만 Element | null 반환
  • 자주 사용하는 요소는 변수에 저장

3. 디버깅 팁

let 요소 = document.querySelector('#test')
console.log(요소)  // null인지 확인
console.log(요소 instanceof HTMLElement)  // 타입 확인
profile
App, Web Developer

0개의 댓글