Web APIs

Web Fundamentals
2025.11.26
Web APIs

웹 앱, 웹 사이트 만들 때 사용할 수 있는 모든 객체 유형
보통 JavaScript와 함께 사용

DOM 상속 구조

[EventTarget] ← [Node] ← [Element] ← [HTMLElement]

[EventTarget] ← [Node] ← [DocumentType] [CharacterData(Text)]

[EventTarget] ← [Window]

DOM 요소 추가

Properties    
textContent Node.textContent = string 모든 콘텐츠 (script, style)
innerHTML Element.innerHTML = htmlString HTML,XML 마크업 → XSS 공격 위험
innerText HTMLElement.innerText = string hidden 요소 제외 → reflow발생
Methods    
appendChild Node.appendChild(Node) 하나의 Node 객체를 자식노드 맨 뒤에 추가
return 반환
append Element.append(Node, String) 하나 이상의 Node 객체 또는 String 객체를
자식노드 맨 뒤에 추가
prepend Element.prepend(Node, String) 하나 이상의 Node 객체 또는 String 객체를
자식노드 맨 앞에 추가

Shadow DOM 만들기

ShadowRoot

[EventTarget] ← [Node] ← [DocumentFragment] ← [ShadowRoot]

element.attachShadow({ mode: 'open' });
  • Shadow Host : attach한 DOM 요소
  • Shadow Tree : Shadow DOM 내부 트리
  • Shadow Root : Shadow Tree의 root node

host CSS

  • 성능 향상을 위해 :host CSS 컨테이너 사용
constructor(){
	super()
	const shadowRoot = this.attachShadow({ mode: 'open' });
	shadowRoot.innerHTML = `
		<style>
			:host{color:#000}
			:host(.class){color:#000}
		</style>
	`
}

닫힌 루트 {mode: 'close'}

  • 사용 지양 (보안 기능이 아님)

사용자 정의 요소 (Custom Elements API)

캡슐화, CustomElementRegistry 객체

pupup-components.js
class myPopup extends HTMLElement{
	connectedCallback(){
		this.innerHTML = `<div>팝업!</div>`
	}
}
customElements.define('my-popup', myPopup);

// Or 익명 클래스 (anonymous class)
customElements.define('my-popup', class extends HTMLElement {
	connectedCallback(){
		this.innerHTML = `<div>팝업!</div>`
	}
});

// Or Shadow DOM 사용자 정의 요소
customElements.define('my-popup', class extends HTMLElement {
	constructor(){
		super();
		
		const shadowRoot = this.attachShadow({ mode: 'open' });
		shadowRoot.innerHTML = `<div>팝업!</div>`
	}
})
index.html
<my-popup></my-popup>

customElements.define() : 사용자 요소 정의

define(name, constructor, options) // CustomElementRegistry method
  • name 사용자 정의 요소 이름
    규칙: 소문자 시작, 하이픈 꼭 포함(HTML 요소와 구분하기 위함)
  • constructor 생성자 class 객체
  • options 선택사항 {extends: "p"}

Constructor (생성자) : 인스턴스 객체 초기화

constructor(){
	super();
}
// Or Shadow DOM 생성
constructor(){
	super();
	this.attachShadow({ mode: 'open' });
}
  • 직접적인 DOM 조작 금지
  • 항상 super() 호출 (부모 생성자 호출 후 this 사용 가능)
    • super() : 상속 받은 부모 클래스(HTMLElement)의 생성자 호출
    • super() 필요한 경우
      • 내부 상태 초기화
      • shadow DOM 생성 시
      • 이벤트 핸들러 사전 바인딩 시
      • 재 렌더링 방지용 flag (this._initialized="flase")

커스텀 요소 수명 주기 (lifecycle callbacks)

  • connectedCallback : DOM 첫 렌더, 요소가(<my-popup>) DOM에 연결될 때마다 호출
    • constructor 사용 안할 시 명시하지 않아도 됨
  • attributeChangedCallback : 속성 변경 시 변경된 부분만 업데이트
  • disconnectedCallback : 요소가 DOM에 연결 해제 되었을 때마다 호출

userAgent

navigator.userAgent

  • 브라우저와 운영체제 정보 문자열
  • 최신 브라우저는 개인정보를 위해 단순화하여 신뢰도 낮음
  • 표준화 되어있지 않기때문에 세밀한 필터 불가
  • 디바이스 감지 정도에만 사용
const isMobile = /iPhone|iPad|iPod|Android|BlackBerry|Windows CE/i.test(navigator.userAgent)

navigator.userAgentData

  • UA 객체
  • 최신 브라우저에서만 사용

window.localStorage

[EventTarget] ← [Window]

Methods  
localStorage.setItem(keyName, keyValue) string, array 불가
localStorage.getItem(keyName) 키 값(string) 반환, 없으면 null
localStorage.removeItem(keyName) 키 값 제거

window.requestAnimationFrame() method

브라우저가 다음 화면을 그리기 직전 콜백 실행 (브라우저 주사율 기준 보통 60Hz)

setInterval 과 비교

  requestAnimationFrame setInterval
실행 타이밍 다음 프레임 직전 지정한 ms 무조건 실행
백그라운드 자동 일시정지 (효율적) 계속 실행 (낭비)
프레임 동기화 정확히 맞춤 (부드러움) 어긋날 수 있음
중지 cancelAnimationFrame(id) clearInterval(id)
const reqAni = () => {
	const currentLeft = parseFloat(~.style.left) || 0
	~.style.left = currentLeft + 10 + 'px'

	if (currentLeft < 300) {
		requestAnimationFrame(reqAni)
		// return 시에도 멈춤
	}
}
reqAni()
const setInt = setInterval(()=>{
	const currentLeft = parseFloat(~.style.left) || 0;
	~.style.left = currentLeft + 10 + 'px';

	if(currentLeft > 300){
		clearInterval(setInt);
	}
}, 16) // 16ms씩 무조건 실행

사이즈와 스크롤

Window Size

Properties  
window.innerWidth
window.innerHeight
브라우저 메뉴바, 툴바, 스크롤바 제외
window.outerWidth
window.outerHeight
브라우저 메뉴바, 툴바, 스크롤바 포함

Size

Properties  
Element.clientWidth
Element.clientHeight
Element + padding (스크롤 바 생길 시 제외)
Element.scrollWidth
Element.scrollHeight
스크롤 영역 밖 모든 콘텐츠 Element + padding (스크롤 바 생길 시 제외)
HTMLElement.offsetWidth
HTMLElement.offsetHeight
Element + padding + border (스크롤 바 생길 시 동일)

Margin size

Window Methods  
HTMLElement.style.marginLeft 인라인 스타일로 지정된 값(String)
예) 20px
getComputedStyle(Element).marginLeft css 포함 적용된 스타일 값(String)

Scroll

Properties  
window.scrollX window.scrollY
=== .pageXOffset .pageYOffset
스크롤 된 픽셀 수
Element.scrollLeft
Element.scrollTop
Element 내부(overflow:auto) scroll 픽셀 수 get, set
설정(set) Element.scrollTop = number
HTMLElement.offsetLeft
HTMLElement.offsetTop
부모 position 기준 상대 좌표
(부모에 position 없을 시 <body> 좌표 또는 <table> 좌표)
Methods  
window(Element).scrollTo(x, y)
.scrollTo({top:y, left:x, behavior: '...'})
=== .scroll()
해당 좌표로 스크롤
window(Element).scrollBy(x, y)
.scrollBy({top:y, left:x, behavior: '...'})
현재 좌표 기준으로 해당 좌표만큼 스크롤
Element.getBoundingClientRect() Element 크기, 위치 정보를 담은 DOMRect 객체 반환
x / left, y / top, right, bottom
width, height, : padding 더한 값

부모에 position 있어도 <body> Scroll 값(절대 값) 사용할 때:

window.scrollY + Element.getBoundingClientRect().top;

key=value 와 option(path, expires) 문자열

set

function setCookie(name, value, setExpires) {
	const numericDays = Number(setExpires) || 0;
	const expireDate = new Date();
	expireDate.setDate(expireDate.getDate() + numericDays);
	document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; expires=${expireDate.toUTCString()}; path=/; SameSite=Lax; Secure`;
}
setCookie('cookieName', 'cookieValue', 7)
  • encodeURIComponent decodeURIComponent

    • .cookie 는 HTTP 헤더 통해 전달: Protocol 준수, 데이터로 인해 쿠키 문자열 형식(= ; , …) 파괴 방지
      ex) name: user=NA=;ME;
  • SameSite : 교차 사이트 요청

    • SameSite=Strict; : 도메인 완전히 일치
    • SameSite=Lax; : iframe, AJAX 호츨에는 전송하지 않음 (일반적)
    • SameSite=None; : 도메인 달라도 무조건 전송, Secure 속성 설정되어야만 작동
  • Secure : HTTPS 연결에만 쿠키 전송

get

function getCookie(name){
	const escapedName = encodeURIComponent(name).replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1');
  const nameMatche = document.cookie.match(new RegExp(
    "(?:^|; )" + escapedName + "=([^;]*)"
  ));
  return nameMatche ? decodeURIComponent(nameMatche[1]) : null;
}
  • $1 : 첫 번째 그룹 (...) 에서 매치된 텍스트 그대로 치환
    ex) * → /* (Escape)
  • escaped 이유: 정규 표현식 정확히(특수 기호 일반 문자열로 변경) 매칭
    ex) name: user.name 일 경우 (패턴 .: 모든 문자 1개 매칭) → user_name user*name 매칭

delete

function deleteCookie(name){
	const initDate = "Thu, 01 Jan 1970 00:00:00 GMT"
	document.cookie = `${encodeURIComponent(name)}=; expires=${initDate}; path=/; SameSite=Lax; Secure`
}
  • 유닉스 타임(Unix Epoch) 시작점
  • setCookie 와 option 동일하게 지정

Geolocation API

현재 위치 가져오는 API (HTTPS에서만 사용 가능)

function succeess(coords) {
	coords.latitude     //위도
	coords.longitude    //경도
}
navigator.geolocation.getCurrentPosition(success, error, [options]);

getAnimations()

요소에 현재 적용 중인 모든 애니메이션 Promise 객체 배열 반환

element.getAnimations()
  • .playbackRate : 재생 속도
  • .finished : 완료 시
  • .cancel() : 실행 중인 모든 애니메이션 종료
element.getAnimations().map(ani => ani.playbackRate = 0.5)

ResizeObserver

대상 요소 크기 변화 비동기적 관찰, 생성자(객체)

Methods  
ResizeObserver.observe(targetElement) 관찰 대상 목록에 요소 추가
ResizeObserver.unobserve(target) 관찰 종료
ResizeObserver.disconnect() 모든 관찰 해제
const resizeObserver = new ResizeObserver(entries => {
	entries.forEach(entry => {
		...entry.contentRect.width
	});
});
resizeObserver.observe(Observe Element);

Intersection Observer API

상위 요소 또는 최상의 문서의 뷰포트와 대상 요소 사이의 변화 비동기적 관찰, 생성자(객체)

  • 이미지 Lazy loading
  • 무한 스크롤
Methods  
IntersectionObserver.observe(targetElement) 주시 대상 목록에 요소 추가
IntersectionObserver.unobserve(target) 주시 중단
IntersectionObserver.disconnect() 모든 주시 대상 해제
Options  
IntersectionObserver.root 바운딩 기준 요소(타겟의 상위 요소). null 이면 브라우저 뷰포트
IntersectionObserver.rootMargin root 요소에 적용될 사이즈 조정 (px, %)
ex) fixed 요소 있을 경우 조정
IntersectionObserver.thresholds oberver의 콜백이 실행되어야하는 가시성 백분율 (0 ~ 1) 0.5 → 50% 보일 때 콜백
const observers = []; // new IntersectionObserver 들 저장 관리, 해제(disconnect) 안할 시 메모리 누수
const observeContent = ()=>{
	const options = {
		root: null,
		rootMargin: `0px 0px 0px 0px`,
		threshold: 0.5
	}
	const observer = new IntersectionObserver(entries =>{
		entries.forEach(entry =>{
			if(entry.isIntersecting){
				...
			}
		})
	}, options)
	observer.observe(Observe Element)
	observers.push(observer)
}
const disconnectObserveContent = ()=>{
	observers.forEach(observer => observer.disconnect());
	observers.length = 0; // 기존 배열 (const observers) 유지하면서 내용만 삭제하고 싶을 때
}

JavaScript

JSON

Methods  
JSON.stringify() Object, array → JSON String
JSON.parse() JSON String → Object, array

Promise()

비동기 작업의 최종 완료 또는 실패 나타내는 객체

new Promise(executor)

Promise State

Pending | Fulfilled | Rejected

생성

const myPromise = new Promise((resolve, reject) =>{
	resolve(value)
	//or
	reject("에러")
})

사용

myPromise
	.then(data => data)
	.catch(error => console.error(error))
	.finally(()=> 작업종료)
async function handlePromise(){
	try{
		const data = await myPromise;

	}catch(error){
		console.error(error)

	}finally{
		작업종료
	}
}
Promise Array 반환 시점 결과
Promise.all() 하나라도 실패 시 바로 reject resolve 배열 또는 reject(첫 번째 reject)
Promise.allSettled() 모든 Promise 처리된 후 상태 포함한 객체 배열
//Promise 중 reject 있을 때
//Promise.all(Promise array)
PromiseState: "rejected"
PromiseResult: 거부 사유

//Promise.allSettled(Promise array)
PromiseState: "fulfilled",
PromiseResult: [{status: 'fulfilled', value: Array...}, {status: 'rejected', reason: 거부 사유}]

© 2026 Choi Seohee. All Rights Reserved.