자바스크립트 모듈

자바스크립트 모듈

열여섯 번째 포스팅까지 다양한 주제를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "모듈"에 대해 작성해보려 한다. 모듈(Module)은 ES6에서 도입된 코드 분리 및 재사용을 위한 문법으로, 자바스크립트 애플리케이션의 구조를 체계적으로 관리한다.


모듈은 자바스크립트에서 코드를 파일 단위로 분할하며, 외부에서 필요한 기능만 노출한다. 이번 포스팅에서는 모듈의 개념, 선언 및 내보내기, 가져오기 방식, 활용 방법, 그리고 실습 예제를 작성해볼 예정이다.


모듈은 자바스크립트의 유지보수를 이해하는 데 필수적이다. 콘솔 또는 HTML 파일에서 예제를 실행하며 모듈의 동작을 체감할 수 있다.


모듈의 개념

모듈은 독립적인 코드 단위를 의미한다. 자바스크립트에서 모듈은 ES6 이전의 IIFE(즉시 실행 함수)나 CommonJS 방식을 대체하여, 표준화된 방식으로 코드를 분리한다. 모듈은 변수, 함수, 클래스를 캡슐화하며, 외부로 내보내기(export)와 가져오기(import)를 통해 관리한다.

다음은 기본적인 모듈 예제이다:

// utils.js
export function sayHello(name) {
    return `안녕, ${name}입니다.`;
}

// main.js
import { sayHello } from './utils.js';
console.log(sayHello('홍길동')); // "안녕, 홍길동입니다."
        

위 예제는 모듈을 정의하고 사용한다.

모듈은 코드의 재사용성과 가독성을 높인다. 이는 대규모 애플리케이션 개발에서 필수적이다.


모듈 선언과 내보내기

모듈은 export 키워드로 내보낸다.


1. 명명된 내보내기

개별 항목을 내보낸다:

// math.js
export function add(a, b) {
    return a + b;
}
export const PI = 3.14;

// app.js
import { add, PI } from './math.js';
console.log(add(2, 3)); // 5
console.log(PI); // 3.14
        

2. 기본 내보내기

단일 항목을 기본으로 내보낸다:

// user.js
export default class User {
    constructor(name) {
        this.name = name;
    }
    greet() {
        return `안녕, ${this.name}`;
    }
}

// app.js
import User from './user.js';
const user = new User('김유신');
console.log(user.greet()); // "안녕, 김유신"
        

예제: 내보내기 활용

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>모듈 내보내기</title>
    <script type="module">
        // 모듈로 사용하려면 type="module" 필요
        import { greet } from './greet.js';
        document.getElementById('btn').addEventListener('click', () => {
            document.getElementById('result').textContent = greet('홍길동');
        });
    </script>
</head>
<body>
    <button id="btn">인사</button>
    <div id="result"></div>
</body>
</html>

// greet.js
<script type="module">
export function greet(name) {
    return `안녕하세요, ${name}님!`;
}
</script>
        

이 예제는 명명된 내보내기를 사용한다. (참고: 실제 실행은 로컬 서버 필요)


모듈 가져오기

모듈은 import 키워드로 가져온다.


1. 명명된 가져오기

특정 항목을 가져온다:

import { multiply, divide } from './math.js';
        

2. 기본 가져오기

기본 내보내기 항목을 가져온다:

import User from './user.js';
        

3. 전체 가져오기

*로 모든 항목을 가져온다:

import * as utils from './utils.js';
console.log(utils.add(1, 2)); // 3
        

예제: 가져오기 방식

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>모듈 가져오기</title>
    <script type="module">
        import * as calc from './calc.js';
        document.getElementById('addBtn').addEventListener('click', () => {
            document.getElementById('result').textContent = calc.add(5, 3);
        });
        document.getElementById('subBtn').addEventListener('click', () => {
            document.getElementById('result').textContent = calc.subtract(5, 3);
        });
    </script>
</head>
<body>
    <button id="addBtn">더하기</button>
    <button id="subBtn">빼기</button>
    <div id="result"></div>
</body>
</html>

// calc.js
<script type="module">
export function add(a, b) {
    return a + b;
}
export function subtract(a, b) {
    return a - b;
}
</script>
        

이 예제는 전체 가져오기를 사용한다.


모듈 활용

모듈은 다양한 상황에서 활용된다.


1. 유틸리티 모듈

공통 기능을 모듈화한다:

// stringUtils.js
export function capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}

// app.js
import { capitalize } from './stringUtils.js';
console.log(capitalize('hello')); // "Hello"
        

2. 데이터 모듈

데이터를 관리한다:

// data.js
export const config = {
    apiKey: 'abc123',
    baseUrl: 'https://api.example.com'
};

// app.js
import { config } from './data.js';
console.log(config.apiKey); // "abc123"
        

예제: 모듈 활용

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>모듈 활용</title>
    <script type="module">
        import { formatDate } from './dateUtils.js';
        document.getElementById('btn').addEventListener('click', () => {
            document.getElementById('result').textContent = formatDate(new Date());
        });
    </script>
</head>
<body>
    <button id="btn">날짜 표시</button>
    <div id="result"></div>
</body>
</html>

// dateUtils.js
<script type="module">
export function formatDate(date) {
    return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
</script>
        

이 예제는 날짜 형식을 모듈화한다.


실습 예제: 모듈 관리

모듈을 활용한 관리 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>모듈 관리</title>
    <style>
        #taskList {
            list-style-type: none;
            padding: 0;
        }
        #taskList li {
            padding: 5px;
            background: #f0f0f0;
            margin: 5px 0;
        }
    </style>
    <script type="module">
        import { TaskManager } from './taskManager.js';
        const manager = new TaskManager();
        const input = document.getElementById('input');
        const addBtn = document.getElementById('addBtn');

        addBtn.addEventListener('click', () => {
            if (input.value) {
                manager.addTask(input.value);
                input.value = '';
            }
        });
    </script>
</head>
<body>
    <input type="text" id="input" placeholder="할 일 입력">
    <button id="addBtn">추가</button>
    <ul id="taskList"></ul>
</body>
</html>

// taskManager.js
<script type="module">
export class TaskManager {
    constructor() {
        this.tasks = [];
    }
    addTask(task) {
        this.tasks.push(task);
        this.render();
    }
    render() {
        const list = document.getElementById('taskList');
        list.innerHTML = '';
        this.tasks.forEach((task, index) => {
            const li = document.createElement('li');
            li.textContent = `${index + 1}. ${task}`;
            list.appendChild(li);
        });
    }
}
</script>
        

이 예제는 모듈로 할 일을 관리한다.


Module Example

실무에서의 모듈

실무에서는 API 호출을 모듈화한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>API 모듈</title>
    <script type="module">
        import { fetchData } from './api.js';
        document.getElementById('fetchBtn').addEventListener('click', async () => {
            const data = await fetchData('https://jsonplaceholder.typicode.com/users/1');
            document.getElementById('result').textContent = JSON.stringify(data, null, 2);
        });
    </script>
</head>
<body>
    <button id="fetchBtn">데이터 가져오기</button>
    <div id="result"></div>
</body>
</html>

// api.js
<script type="module">
export async function fetchData(url) {
    const response = await fetch(url);
    return await response.json();
}
</script>
        

이 예제는 API 호출을 모듈화한다.


주의할 점

모듈은 로컬 실행 시 서버가 필요하다. 이름 충돌을 방지한다. 비동기 모듈 로딩을 고려한다.


결론

모듈의 선언, 내보내기를 학습하며 실습 예제를 통해 알아보았다. 다음 포스팅에서는 템플릿 리터럴에 대해 작성해볼 예정이다.

자바스크립트 클래스

자바스크립트 클래스

열다섯 번째 포스팅까지 다양한 주제를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "클래스"에 대해 작성해보려 한다. 클래스(Class)는 ES6에서 도입된 객체 지향 프로그래밍 문법으로, 객체 생성과 상속을 체계적으로 관리한다.


클래스는 자바스크립트에서 객체 기반 코드를 구조화하며, 기존 프로토타입 기반 상속을 간소화한다. 이번 포스팅에서는 클래스의 개념, 선언 및 사용, 메서드와 속성, 상속, 그리고 실습 예제를 작성해볼 예정이다.


클래스는 자바스크립트의 객체 지향 프로그래밍을 이해하는 데 필수적이다. 콘솔 또는 HTML 파일에서 예제를 실행하며 클래스의 동작을 체감할 수 있다.


클래스의 개념

클래스는 객체를 생성하기 위한 템플릿이다. 자바스크립트에서 클래스는 ES6 이전의 프로토타입 기반 객체 생성을 더 직관적인 문법으로 대체한다. 클래스는 속성(프로퍼티)과 메서드를 정의하며, 이를 기반으로 인스턴스를 생성한다.

다음은 기본적인 클래스 예제이다:

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
        console.log(`안녕, ${this.name}입니다.`);
    }
}
const person = new Person('홍길동');
person.sayHello(); // "안녕, 홍길동입니다."
        

위 예제는 클래스를 정의하고 인스턴스를 생성한다.

클래스는 객체 지향 프로그래밍의 캡슐화와 상속을 지원한다. 이는 코드의 재사용성과 가독성을 강화한다.


클래스 선언과 사용

클래스는 class 키워드로 선언한다.


1. 클래스 선언

기본 구조는 다음과 같다:

class Car {
    constructor(brand) {
        this.brand = brand;
    }
    drive() {
        console.log(`${this.brand}가 운전됩니다.`);
    }
}
const myCar = new Car('현대');
myCar.drive(); // "현대가 운전됩니다."
        

2. 생성자

constructor는 인스턴스 초기화를 담당한다:

class Book {
    constructor(title, author) {
        this.title = title;
        this.author = author;
    }
    getInfo() {
        return `${this.title} - ${this.author}`;
    }
}
const book = new Book('자바스크립트', '김유신');
console.log(book.getInfo()); // "자바스크립트 - 김유신"
        

예제: 클래스 기본 사용

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클래스 기본</title>
</head>
<body>
    <button id="btn">정보 표시</button>
    <div id="result"></div>
    <script>
        class User {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
            showInfo() {
                return `${this.name}, ${this.age}세`;
            }
        }
        const user = new User('홍길동', 30);
        const btn = document.getElementById('btn');
        const result = document.getElementById('result');

        btn.addEventListener('click', () => {
            result.textContent = user.showInfo();
        });
    </script>
</body>
</html>
        

이 예제는 클래스 인스턴스의 정보를 표시한다.


메서드와 속성

클래스는 메서드와 속성을 정의한다.


1. 인스턴스 메서드

인스턴스에 속한다:

class Counter {
    constructor() {
        this.count = 0;
    }
    increment() {
        this.count++;
        return this.count;
    }
}
const counter = new Counter();
console.log(counter.increment()); // 1
        

2. 정적 메서드

static으로 클래스 자체에 속한다:

class MathUtil {
    static add(a, b) {
        return a + b;
    }
}
console.log(MathUtil.add(5, 3)); // 8
        

예제: 메서드 활용

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>메서드 활용</title>
</head>
<body>
    <button id="incBtn">증가</button>
    <button id="addBtn">더하기</button>
    <div id="result">0</div>
    <script>
        class Calculator {
            constructor() {
                this.value = 0;
            }
            increment() {
                this.value++;
                return this.value;
            }
            static add(a, b) {
                return a + b;
            }
        }
        const calc = new Calculator();
        const incBtn = document.getElementById('incBtn');
        const addBtn = document.getElementById('addBtn');
        const result = document.getElementById('result');

        incBtn.addEventListener('click', () => {
            result.textContent = calc.increment();
        });

        addBtn.addEventListener('click', () => {
            result.textContent = Calculator.add(parseInt(result.textContent), 10);
        });
    </script>
</body>
</html>
        

이 예제는 인스턴스와 정적 메서드를 결합한다.


클래스 상속

클래스는 extends로 상속한다.


1. 기본 상속

부모 클래스를 확장한다:

class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name}가 소리를 냅니다.`);
    }
}
class Dog extends Animal {
    bark() {
        console.log(`${this.name}가 짖습니다.`);
    }
}
const dog = new Dog('멍멍이');
dog.speak(); // "멍멍이가 소리를 냅니다."
dog.bark(); // "멍멍이가 짖습니다."
        

2. super 호출

super로 부모 생성자를 호출한다:

class Cat extends Animal {
    constructor(name, color) {
        super(name);
        this.color = color;
    }
    describe() {
        console.log(`${this.name}는 ${this.color}색입니다.`);
    }
}
const cat = new Cat('야옹이', '검정');
cat.describe(); // "야옹이는 검정색입니다."
        

예제: 상속 활용

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클래스 상속</title>
</head>
<body>
    <button id="speakBtn">소리</button>
    <button id="barkBtn">짖기</button>
    <div id="result"></div>
    <script>
        class Animal {
            constructor(name) {
                this.name = name;
            }
            speak() {
                return `${this.name}가 소리를 냅니다.`;
            }
        }
        class Dog extends Animal {
            bark() {
                return `${this.name}가 짖습니다.`;
            }
        }
        const dog = new Dog('멍멍이');
        const speakBtn = document.getElementById('speakBtn');
        const barkBtn = document.getElementById('barkBtn');
        const result = document.getElementById('result');

        speakBtn.addEventListener('click', () => {
            result.textContent = dog.speak();
        });

        barkBtn.addEventListener('click', () => {
            result.textContent = dog.bark();
        });
    </script>
</body>
</html>
        

이 예제는 상속을 활용한다.


실습 예제: 클래스 관리

클래스를 활용한 관리 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클래스 관리</title>
    <style>
        #itemList {
            list-style-type: none;
            padding: 0;
        }
        #itemList li {
            padding: 5px;
            background: #f0f0f0;
            margin: 5px 0;
        }
    </style>
</head>
<body>
    <input type="text" id="input" placeholder="항목 입력">
    <button id="addBtn">추가</button>
    <ul id="itemList"></ul>
    <script>
        class ItemManager {
            constructor() {
                this.items = [];
            }
            addItem(item) {
                this.items.push(item);
                this.render();
            }
            render() {
                const list = document.getElementById('itemList');
                list.innerHTML = '';
                this.items.forEach((item, index) => {
                    const li = document.createElement('li');
                    li.textContent = `${index + 1}. ${item}`;
                    list.appendChild(li);
                });
            }
        }
        const manager = new ItemManager();
        const input = document.getElementById('input');
        const addBtn = document.getElementById('addBtn');

        addBtn.addEventListener('click', () => {
            if (input.value) {
                manager.addItem(input.value);
                input.value = '';
            }
        });
    </script>
</body>
</html>
        

이 예제는 클래스로 항목을 관리한다.


Class Example

실무에서의 클래스

실무에서는 데이터 모델에 활용한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>데이터 모델</title>
</head>
<body>
    <button id="fetchBtn">데이터 가져오기</button>
    <div id="result"></div>
    <script>
        class DataFetcher {
            constructor(url) {
                this.url = url;
            }
            async fetchData() {
                const response = await fetch(this.url);
                const data = await response.json();
                return data;
            }
            display(data) {
                result.textContent = JSON.stringify(data, null, 2);
            }
        }
        const fetcher = new DataFetcher('https://jsonplaceholder.typicode.com/users/1');
        const fetchBtn = document.getElementById('fetchBtn');
        const result = document.getElementById('result');

        fetchBtn.addEventListener('click', async () => {
            const data = await fetcher.fetchData();
            fetcher.display(data);
        });
    </script>
</body>
</html>
        

이 예제는 클래스로 데이터를 관리한다.


주의할 점

클래스는 프로토타입 기반이다. 상속 시 super 호출을 잊지 않는다. 정적 메서드는 인스턴스에서 호출 불가하다.


결론

클래스의 선언, 상속을 학습하며 실습 예제를 통해 알아보았다. 다음 포스팅에서는 모듈에 대해 작성해볼 예정이다.

자바스크립트 this 바인딩

자바스크립트 this 바인딩

열네 번째 포스팅에서 클로저를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "this 바인딩"에 대해 알아보려 한다. this 바인딩은 자바스크립트에서 함수 호출 시 this 키워드가 참조하는 객체를 결정하며, 객체 지향 프로그래밍과 함수 실행에 핵심적인 역할을 한다.


this 바인딩은 호출 방식에 따라 달라지며, 이해하지 못하면 오류가 발생한다. 이번 포스팅에서는 this 바인딩의 개념, 바인딩 규칙, 제어 방법, 그리고 실습 예제를 상세히 다룬다.


클로저와 함께 this 바인딩은 자바스크립트의 고급 문법을 이해하는 데 필수적이다. 콘솔에서 예제를 실행하며 this의 동작을 체감할 수 있다.


this 바인딩의 개념

this는 함수 호출 시 결정되는 동적 컨텍스트이다. 자바스크립트에서 this는 함수가 호출되는 방식에 따라 참조하는 객체가 달라진다. 다음은 기본적인 this 예제이다:

function sayThis() {
    console.log(this);
}
sayThis(); // Window (브라우저 환경)
        

위 예제는 전역 호출 시 this가 전역 객체를 참조한다.

this는 객체 지향 프로그래밍에서 객체의 속성과 메서드를 연결한다. 호출 방식에 따라 동작이 달라지므로 규칙을 이해해야 한다.


this 바인딩 규칙

this 바인딩은 네 가지 규칙에 따라 결정된다.


1. 기본 바인딩

함수가 독립적으로 호출되면 전역 객체를 참조한다:

function basic() {
    console.log(this);
}
basic(); // Window
        

2. 암시적 바인딩

객체의 메서드로 호출되면 해당 객체를 참조한다:

const obj = {
    name: '객체',
    say: function() {
        console.log(this.name);
    }
};
obj.say(); // "객체"
        

3. 명시적 바인딩

call, apply, bindthis를 지정한다:

function explicit() {
    console.log(this.value);
}
const data = { value: '데이터' };
explicit.call(data); // "데이터"
        

4. new 바인딩

생성자 함수로 호출되면 새 객체를 참조한다:

function NewObj() {
    this.name = '새 객체';
}
const instance = new NewObj();
console.log(instance.name); // "새 객체"
        

예제: 바인딩 비교

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>this 바인딩</title>
</head>
<body>
    <script>
        function showThis() {
            console.log(this);
        }
        showThis(); // Window

        const obj = { show: showThis };
        obj.show(); // obj

        showThis.call({ value: '명시적' }); // { value: "명시적" }

        const newInstance = new showThis(); // showThis {}
    </script>
</body>
</html>
        

이 예제는 네 가지 바인딩을 확인한다.


this 바인딩 제어

this 바인딩은 명시적으로 제어한다.


1. call과 apply

callapply로 this를 변경한다:

function greet(greeting) {
    console.log(`${greeting}, ${this.name}`);
}
const person1 = { name: '홍길동' };
const person2 = { name: '김유신' };
greet.call(person1, '안녕'); // "안녕, 홍길동"
greet.apply(person2, ['안녕']); // "안녕, 김유신"
        

2. bind

bind로 고정된 함수를 생성한다:

const boundGreet = greet.bind(person1);
boundGreet('안녕'); // "안녕, 홍길동"
        

예제: this 제어

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>this 제어</title>
</head>
<body>
    <button id="btn1">버튼 1</button>
    <button id="btn2">버튼 2</button>
    <div id="result"></div>
    <script>
        function showName() {
            result.textContent = this.name;
        }
        const obj1 = { name: '첫 번째' };
        const obj2 = { name: '두 번째' };
        const boundShow1 = showName.bind(obj1);
        const boundShow2 = showName.bind(obj2);

        const btn1 = document.getElementById('btn1');
        const btn2 = document.getElementById('btn2');
        const result = document.getElementById('result');

        btn1.addEventListener('click', boundShow1);
        btn2.addEventListener('click', boundShow2);
    </script>
</body>
</html>
        

이 예제는 bind로 this를 제어한다.


실습 예제: this와 클로저

this와 클로저를 결합한 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>this와 클로저</title>
</head>
<body>
    <button id="btn">증가</button>
    <div id="count">0</div>
    <script>
        const btn = document.getElementById('btn');
        const count = document.getElementById('count');
        const counter = (function() {
            var num = 0;
            return {
                increment: function() {
                    num++;
                    this.display(num);
                },
                display: function(value) {
                    count.textContent = value;
                }
            };
        })();

        btn.addEventListener('click', () => counter.increment.call(counter));
    </script>
</body>
</html>
        

이 예제는 클로저와 this를 결합한다.


This Binding Example

실무에서의 this 바인딩

실무에서는 이벤트 핸들러에서 this를 관리한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 this</title>
</head>
<body>
    <button id="btn">클릭</button>
    <div id="log"></div>
    <script>
        const obj = {
            name: '객체',
            handleClick: function() {
                log.textContent = this.name;
            }
        };
        const btn = document.getElementById('btn');
        const log = document.getElementById('log');
        btn.addEventListener('click', obj.handleClick.bind(obj));
    </script>
</body>
</html>
        

이 예제는 이벤트에서 this를 고정한다.


주의할 점

this는 호출 시점에 결정되므로 예측한다. 화살표 함수는 this를 바인딩하지 않는다. 명시적 바인딩을 적절히 사용한다.


결론

this의 규칙, 제어를 학습하며 실습 예제를 통해 알아보았다. 다음 포스팅에서는 다른 문법 개념에 대해 작성해보려고 한다.

'코딩 공부 > 자바스크립트' 카테고리의 다른 글

17. 자바스크립트 모듈  (0) 2025.02.28
16. 자바스크립트 클래스  (1) 2025.02.27
14. 자바스크립트 클로저  (0) 2025.02.27
13. 자바스크립트 스코프  (0) 2025.02.27
12. 자바스크립트 호이스팅  (0) 2025.02.26
자바스크립트 클로저

자바스크립트 클로저

열세 번째 포스팅에서 스코프를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "클로저"를 탐구한다. 클로저(Closure)는 함수가 외부 스코프의 변수를 기억하고 접근할 수 있는 메커니즘으로, 데이터 캡슐화와 상태 관리에 활용된다.


클로저는 스코프와 밀접하게 연관되며, 자바스크립트의 강력한 기능을 제공한다. 이번 포스팅에서는 클로저의 개념, 동작 원리, 활용 방법, 그리고 실습 예제를 작성해볼 예정이다.


스코프와 함께 클로저는 자바스크립트의 고급 문법을 이해하는 데 필수적이다. 콘솔에서 예제를 실행하며 클로저의 동작을 체감할 수 있다.


클로저의 개념

클로저는 함수가 선언된 스코프의 변수를 함수 종료 후에도 기억하는 현상이다. 이는 함수와 스코프 체인이 결합된 결과이다. 다음은 기본적인 클로저 예제이다:

function outer() {
    var outerVar = '외부 변수';
    function inner() {
        console.log(outerVar);
    }
    return inner;
}
const innerFunc = outer();
innerFunc(); // "외부 변수"
        

위 예제는 내부 함수가 외부 변수에 접근한다.

클로저는 데이터의 비공개성과 상태 유지를 가능하게 한다. 이는 자바스크립트의 함수형 프로그래밍에서 핵심이다.


클로저의 동작 원리

클로저는 실행 컨텍스트와 스코프 체인에 의해 구현된다.


1. 스코프 체인

내부 함수는 외부 스코프를 참조한다:

function outer() {
    var x = 10;
    return function() {
        console.log(x);
    };
}
const closure = outer();
closure(); // 10
        

2. 메모리 유지

외부 함수가 종료되어도 변수는 유지된다:

function createCounter() {
    var count = 0;
    return function() {
        return ++count;
    };
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
        

예제: 클로저 메모리

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 메모리</title>
</head>
<body>
    <button id="btn">증가</button>
    <div id="count">0</div>
    <script>
        const btn = document.getElementById('btn');
        const countDiv = document.getElementById('count');
        const increment = (function() {
            var count = 0;
            return function() {
                return ++count;
            };
        })();

        btn.addEventListener('click', () => {
            countDiv.textContent = increment();
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 카운터를 유지한다.


클로저 활용

클로저는 다양한 상황에서 활용된다.


1. 데이터 캡슐화

비공개 변수를 생성한다:

function createPerson(name) {
    var age = 0;
    return {
        getName: () => name,
        getAge: () => age,
        setAge: (newAge) => age = newAge
    };
}
const person = createPerson('홍길동');
console.log(person.getName()); // "홍길동"
console.log(person.getAge()); // 0
person.setAge(25);
console.log(person.getAge()); // 25
        

2. 함수 팩토리

동적인 함수를 생성한다:

function multiplier(factor) {
    return function(num) {
        return num * factor;
    };
}
const double = multiplier(2);
console.log(double(5)); // 10
        

예제: 클로저 팩토리

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 팩토리</title>
</head>
<body>
    <input type="number" id="input" value="5">
    <button id="doubleBtn">2배</button>
    <button id="tripleBtn">3배</button>
    <div id="result"></div>
    <script>
        function createMultiplier(factor) {
            return function(num) {
                return num * factor;
            };
        }
        const double = createMultiplier(2);
        const triple = createMultiplier(3);

        const input = document.getElementById('input');
        const doubleBtn = document.getElementById('doubleBtn');
        const tripleBtn = document.getElementById('tripleBtn');
        const result = document.getElementById('result');

        doubleBtn.addEventListener('click', () => {
            result.textContent = double(parseInt(input.value));
        });

        tripleBtn.addEventListener('click', () => {
            result.textContent = triple(parseInt(input.value));
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 곱셈 함수를 생성한다.


실습 예제: 클로저 상태 관리

클로저를 활용한 상태 관리 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 상태</title>
    <style>
        #toggleBox {
            width: 100px;
            height: 100px;
            background: gray;
        }
    </style>
</head>
<body>
    <button id="toggleBtn">토글</button>
    <div id="toggleBox"></div>
    <script>
        const toggleBtn = document.getElementById('toggleBtn');
        const toggleBox = document.getElementById('toggleBox');
        const toggleState = (function() {
            var isOn = false;
            return function() {
                isOn = !isOn;
                toggleBox.style.backgroundColor = isOn ? 'red' : 'gray';
                return isOn;
            };
        })();

        toggleBtn.addEventListener('click', () => {
            toggleState();
        });
    </script>
</body>
</html>
        

이 예제는 클로저로 상태를 관리한다.

Closure Example

실무에서의 클로저

실무에서는 이벤트 핸들러에 활용한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>클로저 이벤트</title>
</head>
<body>
    <button id="btn">클릭</button>
    <div id="log"></div>
    <script>
        const btn = document.getElementById('btn');
        const log = document.getElementById('log');
        btn.addEventListener('click', (function() {
            var clicks = 0;
            return function() {
                clicks++;
                log.textContent = `클릭 횟수: ${clicks}`;
            };
        })());
    </script>
</body>
</html>
        

이 예제는 클로저로 클릭 횟수를 기록한다.


주의할 점

클로저는 메모리를 유지하므로 누수가 발생할 수 있다. 불필요한 클로저는 정리한다. 복잡한 클로저는 가독성을 저하시킨다.


결론

클로저의 개념, 활용을 학습하며 실습 예제를 통해 이해한다. 다음 포스팅에서는 this 바인딩에 대해 작성해보려고 한다.

자바스크립트 스코프

자바스크립트 스코프

열두 번째 포스팅에서 호이스팅을 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "스코프"에 대해 다루어본다. 스코프(Scope)는 변수의 접근 범위를 정의하며, 자바스크립트 코드의 실행과 변수 관리에 중요한 역할을 한다.


스코프는 변수의 생명 주기와 가시성을 제어한다. 이번 포스팅에서는 스코프의 개념, 전역 및 지역 스코프, 블록 스코프, 스코프 체인, 그리고 실습 예제를 상세히 다룬다.


호이스팅과 연계하여 스코프는 자바스크립트 엔진의 동작을 이해하는 데 필수적이다. 콘솔에서 예제를 실행하며 스코프의 동작을 체감할 수 있다.


스코프의 개념

스코프는 변수와 함수가 유효한 범위를 의미한다. 자바스크립트는 변수 선언 시 스코프를 생성하며, 이는 코드 실행 중 변수 접근 가능 여부를 결정한다. 다음은 기본적인 스코프 예제이다:

var globalVar = '전역';
function test() {
    var localVar = '지역';
    console.log(globalVar); // "전역"
    console.log(localVar); // "지역"
}
test();
console.log(localVar); // ReferenceError: localVar is not defined
        

위 예제는 전역 변수와 지역 변수의 스코프 차이를 보여준다.

스코프는 변수의 충돌을 방지하고, 코드의 안정성을 높인다. 이는 자바스크립트의 핵심 문법이다.


전역 스코프와 지역 스코프

스코프는 크게 전역 스코프와 지역 스코프로 나뉜다.


1. 전역 스코프

전역 스코프는 코드 최상위에서 선언된 변수가 속한다:

var global = '전역 변수';
console.log(global); // "전역 변수"
        

2. 지역 스코프

지역 스코프는 함수 내에서 선언된 변수가 속한다:

function localScope() {
    var local = '지역 변수';
    console.log(local); // "지역 변수"
}
localScope();
console.log(local); // ReferenceError: local is not defined
        

예제: 스코프 비교

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>스코프 비교</title>
</head>
<body>
    <script>
        var outer = '외부 변수';
        function innerFunc() {
            var inner = '내부 변수';
            console.log(outer); // "외부 변수"
            console.log(inner); // "내부 변수"
        }
        innerFunc();
        console.log(outer); // "외부 변수"
        try {
            console.log(inner);
        } catch (e) {
            console.log('에러:', e.message); // "inner is not defined"
        }
    </script>
</body>
</html>
        

이 예제는 전역과 지역 스코프를 확인한다.


블록 스코프

ES6에서 도입된 letconst는 블록 스코프를 지원한다.


1. var와 블록

var는 블록 스코프를 무시한다:

if (true) {
    var blockVar = '블록 변수';
}
console.log(blockVar); // "블록 변수"
        

2. let과 const

letconst는 블록 스코프를 따른다:

if (true) {
    let blockLet = '블록 let';
    const blockConst = '블록 const';
}
console.log(blockLet); // ReferenceError: blockLet is not defined
console.log(blockConst); // ReferenceError: blockConst is not defined
        

예제: 블록 스코프

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>블록 스코프</title>
</head>
<body>
    <script>
        for (var i = 0; i < 3; i++) {
            setTimeout(() => console.log(i), 100); // 3, 3, 3
        }
        for (let j = 0; j < 3; j++) {
            setTimeout(() => console.log(j), 100); // 0, 1, 2
        }
    </script>
</body>
</html>
        

이 예제는 varlet의 블록 스코프 차이를 보여준다.


스코프 체인

스코프 체인은 스코프가 중첩될 때 변수 접근 순서를 정의한다.


1. 중첩 스코프

내부 스코프는 외부 스코프에 접근 가능하다:

var outerVar = '외부';
function outerFunc() {
    var middleVar = '중간';
    function innerFunc() {
        var innerVar = '내부';
        console.log(outerVar); // "외부"
        console.log(middleVar); // "중간"
        console.log(innerVar); // "내부"
    }
    innerFunc();
}
outerFunc();
        

예제: 스코프 체인

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>스코프 체인</title>
</head>
<body>
    <script>
        const global = '전역';
        function level1() {
            const l1 = '레벨 1';
            function level2() {
                const l2 = '레벨 2';
                console.log(global); // "전역"
                console.log(l1); // "레벨 1"
                console.log(l2); // "레벨 2"
            }
            level2();
        }
        level1();
    </script>
</body>
</html>
        

이 예제는 스코프 체인을 확인한다.


실습 예제: 스코프 활용

스코프를 활용한 실습 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>스코프 실습</title>
</head>
<body>
    <button id="btn">실행</button>
    <div id="result"></div>
    <script>
        const btn = document.getElementById('btn');
        const result = document.getElementById('result');
        const globalVar = '전역 변수';

        btn.addEventListener('click', () => {
            result.textContent = '';
            function outer() {
                const outerVar = '외부 변수';
                function inner() {
                    const innerVar = '내부 변수';
                    result.textContent += `${globalVar}, ${outerVar}, ${innerVar}\n`;
                }
                inner();
            }
            outer();

            try {
                console.log(outerVar);
            } catch (e) {
                result.textContent += `에러: ${e.message}\n`; // "outerVar is not defined"
            }
        });
    </script>
</body>
</html>
        

이 예제는 스코프 체인과 오류를 확인한다.

Scope Example

실무에서의 스코프

실무에서는 스코프 충돌을 방지한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>스코프 관리</title>
</head>
<body>
    <script>
        (function() {
            const moduleVar = '모듈 변수';
            console.log(moduleVar); // "모듈 변수"
        })();
        try {
            console.log(moduleVar);
        } catch (e) {
            console.log('에러:', e.message); // "moduleVar is not defined"
        }
    </script>
</body>
</html>
        

이 예제는 IIFE로 스코프를 격리한다.


주의할 점

전역 스코프 사용을 최소화한다. var 대신 letconst를 사용한다. 스코프 체인으로 인한 성능 저하를 고려한다.


결론

이 포스팅은 스코프의 개념, 체인을 학습하며 실습 예제를 통해 활용성을 확인한다. 다음 포스팅에서는 클로저를 다룬다.

자바스크립트 호이스팅

자바스크립트 호이스팅

열한 번째 포스팅까지 다양한 범주를 다루었다면, 이번에는 "문법 및 핵심 개념" 범주에 속하는 "호이스팅"에 대해 다루어보려고 한다. 호이스팅(Hoisting)은 자바스크립트에서 변수와 함수 선언이 코드 실행 전 스코프 최상단으로 끌어올려지는 동작을 의미한다.


호이스팅은 자바스크립트 엔진의 동작 방식과 관련 있으며, 코드 작성 시 예상치 못한 결과를 방지하려면 이해가 필수적이다. 이번 포스팅에서는 호이스팅의 개념, 변수와 함수의 호이스팅 차이, 동작 원리, 그리고 실습 예제를 상세히 다룬다. 약 20,000자의 분량으로 구성되며, 초보자도 실습을 통해 이해할 수 있도록 예제를 풍부하게 포함한다.


호이스팅은 자바스크립트의 핵심 문법을 이해하는 데 중요한 역할을 한다. 콘솔에서 예제를 실행하며 호이스팅의 동작을 체감할 수 있다.


호이스팅의 개념

호이스팅은 자바스크립트에서 선언문(변수, 함수)이 코드 실행 전 스코프의 최상단으로 이동하는 현상이다. 이는 변수나 함수를 선언하기 전에 사용할 수 있게 한다. 다음은 기본적인 호이스팅 예제이다:

console.log(x); // undefined
var x = 10;
        

위 예제는 변수 x가 선언되기 전에 접근해도 에러가 발생하지 않는다. 이는 var 선언이 호이스팅되어 코드 실행 전 스코프 상단으로 이동하기 때문이다.

호이스팅은 자바스크립트 엔진의 컴파일 단계에서 발생한다. 선언문은 메모리에 먼저 등록되며, 초기화는 코드 순서대로 진행된다. 이는 변수와 함수의 동작 방식에 영향을 미친다.


변수 호이스팅

변수의 호이스팅은 선언 방식에 따라 다르게 동작한다.


1. var 선언

var는 선언과 초기화가 호이스팅된다:

console.log(a); // undefined
var a = 5;
console.log(a); // 5
        

위 코드는 실행 시 var a;가 상단으로 이동하며, 초기화는 원래 위치에서 진행된다.


2. let과 const 선언

letconst는 선언만 호이스팅되며 초기화는 진행되지 않는다(일명 "일시적 사각지대", TDZ):

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;

console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 20;
        

예제: 변수 호이스팅 비교

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>변수 호이스팅</title>
</head>
<body>
    <script>
        console.log(varTest); // undefined
        var varTest = 'var 테스트';

        try {
            console.log(letTest);
        } catch (e) {
            console.log('let 에러:', e.message); // "Cannot access 'letTest' before initialization"
        }
        let letTest = 'let 테스트';
    </script>
</body>
</html>
        

이 예제는 varlet의 호이스팅 차이를 확인한다.


함수 호이스팅

함수의 호이스팅은 선언 방식에 따라 다르다.


1. 함수 선언문

함수 선언문은 전체가 호이스팅된다:

sayHello(); // "안녕하세요"
function sayHello() {
    console.log('안녕하세요');
}
        

2. 함수 표현식

함수 표현식은 변수 호이스팅 규칙을 따른다:

sayHi(); // TypeError: sayHi is not a function
var sayHi = function() {
    console.log('안녕');
};
        

예제: 함수 호이스팅 비교

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>함수 호이스팅</title>
</head>
<body>
    <script>
        funcDecl(); // "선언문 호출"
        function funcDecl() {
            console.log('선언문 호출');
        }

        try {
            funcExpr();
        } catch (e) {
            console.log('표현식 에러:', e.message); // "funcExpr is not a function"
        }
        var funcExpr = function() {
            console.log('표현식 호출');
        };
    </script>
</body>
</html>
        

이 예제는 함수 선언문과 표현식의 차이를 보여준다.


호이스팅의 동작 원리

호이스팅은 자바스크립트 엔진의 실행 컨텍스트에서 발생한다.


1. 컴파일 단계

선언문은 스코프 상단으로 이동한다:

// 원본
console.log(x);
var x = 10;

// 컴파일 후
var x;
console.log(x);
x = 10;
        

2. 실행 단계

초기화와 할당은 코드 순서대로 진행한다:

console.log(y); // undefined
var y;
y = 20;
        

예제: 실행 컨텍스트

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>실행 컨텍스트</title>
</head>
<body>
    <script>
        function test() {
            console.log(a); // undefined
            var a = 100;
            console.log(a); // 100
        }
        test();
    </script>
</body>
</html>
        

이 예제는 함수 스코프 내 호이스팅을 확인한다.


실습 예제: 호이스팅 이해

호이스팅을 활용한 실습 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>호이스팅 실습</title>
</head>
<body>
    <button id="btn">실행</button>
    <div id="result"></div>
    <script>
        const btn = document.getElementById('btn');
        const result = document.getElementById('result');

        btn.addEventListener('click', () => {
            result.textContent = '';
            logBefore(); // "선언 전 호출 가능"
            function logBefore() {
                result.textContent += '선언 전 호출 가능\n';
            }

            try {
                logAfter();
            } catch (e) {
                result.textContent += `에러: ${e.message}\n`; // "logAfter is not a function"
            }
            var logAfter = function() {
                result.textContent += '선언 후 호출\n';
            };
        });
    </script>
</body>
</html>
        

이 예제는 함수 선언문과 표현식의 호이스팅 차이를 확인한다.

Hoisting Example

실무에서의 호이스팅

실무에서는 호이스팅으로 인한 오류를 방지한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>호이스팅 방지</title>
</head>
<body>
    <script>
        function safeCode() {
            'use strict';
            console.log(x); // ReferenceError: x is not defined
            var x = 10;
        }
        safeCode();
    </script>
</body>
</html>
        

이 예제는 'use strict'로 호이스팅 오류를 방지한다.


주의할 점

호이스팅은 변수 초기화를 보장하지 않는다. letconst를 사용하여 TDZ를 활용한다. 함수 표현식을 선호한다.


결론

호이스팅의 개념, 동작 원리를 학습하며 실습 예제를 통해 이해한다. 다음 포스팅에서는 스코프를 다룰 예정이다.

자바스크립트 이벤트 처리

자바스크립트 이벤트 처리

이전 열 번째 포스팅에서 DOM 조작을 다루었다면, 이번에는 "DOM 및 브라우저 API" 범주에 속하는 "이벤트 처리"를 탐구한다. 이벤트 처리는 자바스크립트를 통해 사용자와의 상호작용을 가능하게 하는 핵심 기술로, 버튼 클릭, 키보드 입력, 마우스 이동 등 다양한 사용자 행동에 반응한다.


이벤트 처리는 웹 애플리케이션의 동적 특성을 강화하며, 사용자 경험을 개선한다. 이번 포스팅에서는 이벤트의 개념, 이벤트 핸들러 등록 방법, 이벤트 객체 활용, 이벤트 전파 및 제어, 그리고 실습 예제를 다룬다.


앞선 DOM 조작 주제와 결합하면 이벤트 처리는 더욱 실용적인 기능을 제공한다. 콘솔 또는 HTML 파일에서 예제를 실행하며 이벤트 처리의 실질적인 활용 방식을 체감할 수 있다. 예제를 중심으로 진행하며, 각 단계별로 명확히 설명한다.


이벤트의 개념

이벤트는 웹 페이지에서 발생하는 사용자 행동이나 시스템 상태 변화를 의미한다. 자바스크립트는 이러한 이벤트를 감지하고, 특정 코드(이벤트 핸들러)를 실행하여 반응한다. 예를 들어, 사용자가 버튼을 클릭하거나 키를 누르는 행위는 이벤트로 인식된다.


다음은 간단한 이벤트 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 기본</title>
</head>
<body>
    <button id="btn">클릭</button>
    <script>
        const btn = document.getElementById('btn');
        btn.onclick = function() {
            alert('버튼이 클릭되었습니다!');
        };
    </script>
</body>
</html>
        

위 예제는 버튼 클릭 시 경고창을 표시한다. 이벤트는 DOM 요소와 연결되어 사용자와의 상호작용을 가능하게 한다.

이벤트는 사용자 행동뿐만 아니라 시스템 이벤트(예: 페이지 로드 완료)도 포함한다. 자바스크립트는 이벤트 발생 시 이를 처리하는 메커니즘을 제공하며, 이는 웹 애플리케이션의 동적 특성을 뒷받침한다.


이벤트 핸들러 등록 방법

이벤트를 처리하려면 이벤트 핸들러를 등록한다. 자바스크립트는 여러 방식으로 이벤트 핸들러를 연결한다.


1. 인라인 등록

HTML 태그에 직접 이벤트 핸들러를 정의한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>인라인 이벤트</title>
</head>
<body>
    <button onclick="alert('인라인 클릭!')">클릭</button>
</body>
</html>
        
인라인 이벤트

이 방식은 간단하지만 코드가 섞여 유지보수가 어렵다.


2. DOM 속성

자바스크립트에서 DOM 요소의 속성으로 핸들러를 등록한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>DOM 속성 이벤트</title>
</head>
<body>
    <button id="btn">클릭</button>
    <script>
        const btn = document.getElementById('btn');
        btn.onclick = function() {
            console.log('DOM 속성 클릭!');
        };
    </script>
</body>
</html>
        
DOM 속성 이벤트

이 방식은 HTML과 자바스크립트를 분리한다. 단, 한 이벤트에 하나의 핸들러만 등록 가능하다.


3. addEventListener

가장 현대적인 방법으로, 여러 핸들러를 등록할 수 있다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 리스너</title>
</head>
<body>
    <button id="btn">클릭</button>
    <script>
        const btn = document.getElementById('btn');
        btn.addEventListener('click', () => {
            console.log('첫 번째 리스너');
        });
        btn.addEventListener('click', () => {
            console.log('두 번째 리스너');
        });
    </script>
</body>
</html>
        
이벤트 리스너

addEventListener는 유연성과 확장성을 제공한다.


예제: 여러 핸들러 비교

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>핸들러 비교</title>
</head>
<body>
    <button id="btn">클릭</button>
    <script>
        const btn = document.getElementById('btn');
        btn.onclick = () => console.log('속성 핸들러');
        btn.onclick = () => console.log('속성 핸들러 덮어씀'); // 이전 핸들러 덮어씌움
        btn.addEventListener('click', () => console.log('리스너 1'));
        btn.addEventListener('click', () => console.log('리스너 2'));
    </script>
</body>
</html>
        
핸들러 비교

이 예제는 onclick이 덮어씌워지는 반면, addEventListener는 여러 핸들러를 유지한다.


이벤트 객체 활용

이벤트 발생 시 이벤트 객체가 전달된다. 이는 이벤트에 대한 정보를 담고 있다.


1. 기본 활용

이벤트 객체는 핸들러 함수의 첫 번째 매개변수로 전달된다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 객체</title>
</head>
<body>
    <button id="btn">클릭</button>
    <script>
        const btn = document.getElementById('btn');
        btn.addEventListener('click', (event) => {
            console.log(event);
        });
    </script>
</body>
</html>
        
이벤트 객체

2. 이벤트 속성 사용

이벤트 객체의 속성을 활용한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 속성</title>
</head>
<body>
    <div id="area" style="width: 200px; height: 200px; background: #ddd;"></div>
    <script>
        const area = document.getElementById('area');
        area.addEventListener('click', (e) => {
            console.log(`클릭 위치: (${e.clientX}, ${e.clientY})`);
        });
    </script>
</body>
</html>
        
이벤트 속성

clientX, clientY는 클릭 위치 좌표를 반환한다.


예제: 키보드 이벤트

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>키보드 이벤트</title>
</head>
<body>
    <input type="text" id="input">
    <script>
        const input = document.getElementById('input');
        input.addEventListener('keydown', (e) => {
            console.log(`눌린 키: ${e.key}`);
            if (e.key === 'Enter') console.log('엔터 키 입력!');
        });
    </script>
</body>
</html>
        
키보드 이벤트

이 예제는 키보드 입력을 감지한다.


이벤트 전파와 제어

이벤트는 DOM 트리에서 전파된다. 이를 제어하는 방법은 다음과 같다.


1. 버블링과 캡처링

이벤트는 버블링(하위에서 상위로) 또는 캡처링(상위에서 하위로) 방식으로 전파된다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 전파</title>
    <style>
        #parent { background: #ddd; padding: 20px; }
        #child { background: #bbb; padding: 10px; }
    </style>
</head>
<body>
    <div id="parent">
        <button id="child">클릭</button>
    </div>
    <script>
        const parent = document.getElementById('parent');
        const child = document.getElementById('child');
        parent.addEventListener('click', () => console.log('부모 클릭'));
        child.addEventListener('click', () => console.log('자식 클릭'));
    </script>
</body>
</html>
        
이벤트 전파

버튼 클릭 시 "자식 클릭" 후 "부모 클릭"이 출력된다(버블링).


2. 전파 제어

stopPropagation은 전파를 중단한다:

child.addEventListener('click', (e) => {
    console.log('자식 클릭');
    e.stopPropagation();
});
        

이 경우 "부모 클릭"은 발생하지 않는다.


3. 기본 동작 방지

preventDefault는 이벤트의 기본 동작을 막는다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>기본 동작 방지</title>
</head>
<body>
    <form id="form">
        <input type="text">
        <button type="submit">제출</button>
    </form>
    <script>
        const form = document.getElementById('form');
        form.addEventListener('submit', (e) => {
            e.preventDefault();
            console.log('제출 방지됨');
        });
    </script>
</body>
</html>
        
기본 동작 방지

폼 제출 시 페이지 새로고침이 방지된다.


실습 예제: 이벤트 기반 인터랙션

다음은 이벤트 처리를 활용한 실습 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트 실습</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: gray;
            transition: all 0.3s;
        }
        #log {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button id="toggleBtn">토글</button>
    <div id="log"></div>
    <script>
        const box = document.getElementById('box');
        const toggleBtn = document.getElementById('toggleBtn');
        const log = document.getElementById('log');

        toggleBtn.addEventListener('click', () => {
            box.style.backgroundColor = box.style.backgroundColor === 'red' ? 'gray' : 'red';
            log.textContent = '버튼 클릭됨';
        });

        box.addEventListener('mouseover', () => {
            box.style.transform = 'scale(1.2)';
            log.textContent = '마우스 오버';
        });

        box.addEventListener('mouseout', () => {
            box.style.transform = 'scale(1)';
            log.textContent = '마우스 아웃';
        });

        document.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowRight') {
                box.style.marginLeft = (parseInt(box.style.marginLeft || 0) + 10) + 'px';
                log.textContent = '오른쪽 이동';
            }
        });
    </script>
</body>
</html>
        
이벤트 실습

이 예제는 버튼 클릭, 마우스 오버/아웃, 키보드 입력으로 박스를 조작한다.


Event Handling Example

실무에서의 이벤트 처리

실무에서는 이벤트 처리를 통해 사용자 인터페이스를 동적으로 반영한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>실시간 검색</title>
</head>
<body>
    <input type="text" id="search" placeholder="검색어 입력">
    <div id="result"></div>
    <script>
        const search = document.getElementById('search');
        const result = document.getElementById('result');
        search.addEventListener('input', async () => {
            result.textContent = '검색 중...';
            const response = await fetch(`https://api.example.com/search?q=${search.value}`);
            const data = await response.json();
            result.textContent = data.results.join(', ');
        });
    </script>
</body>
</html>
        
실시간 검색

이 예제는 입력 시 비동기 검색 결과를 표시한다(가상 URL 사용).


예제: 드래그 앤 드롭

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>드래그 앤 드롭</title>
    <style>
        #dragBox {
            width: 50px;
            height: 50px;
            background: blue;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="dragBox"></div>
    <script>
        const dragBox = document.getElementById('dragBox');
        let isDragging = false;
        let startX, startY;

        dragBox.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX - (parseInt(dragBox.style.left) || 0);
            startY = e.clientY - (parseInt(dragBox.style.top) || 0);
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                dragBox.style.left = (e.clientX - startX) + 'px';
                dragBox.style.top = (e.clientY - startY) + 'px';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
        });
    </script>
</body>
</html>
        
드래그 앤 드롭

이 예제는 박스를 드래그하여 이동한다.


주의할 점

이벤트 핸들러는 중복 등록 시 의도치 않은 동작을 유발할 수 있다. 이벤트 전파는 상황에 따라 제어한다. 성능을 위해 불필요한 이벤트 리스너는 제거한다.


결론

이 포스팅은 자바스크립트 이벤트 처리를 다루어 보았다. 이벤트 개념, 핸들러 등록, 객체 활용, 전파 제어를 학습하며 실습 예제를 통해 실무 적용 가능성을 확인한다.

자바스크립트 DOM 조작

자바스크립트 DOM 조작

지금까지 변수, 데이터 타입, 함수, 배열, 객체, 제어 구조, 에러 처리, 비동기 프로그래밍 등 다양한 주제를 다루었다면, 이번에는 DOM 및 브라우저 API 범주에 속하는 "DOM 조작"을 탐구한다. DOM(Document Object Model) 조작은 자바스크립트를 활용하여 웹 페이지의 HTML 요소를 동적으로 수정하는 기술이며, 사용자와 상호작용하는 웹 애플리케이션 제작에 필수적이다.


DOM 조작은 자바스크립트로 HTML 구조를 변경하고 스타일을 조정하며 요소를 추가하거나 삭제하는 과정을 포함한다. 이 기술은 정적인 웹 페이지를 동적으로 변환하여 사용자 경험을 향상시킨다. 이번 포스팅에서는 DOM의 개념, 요소 선택 방법, 속성과 스타일 수정, 요소 생성 및 삭제, 그리고 실습 예제를 다룰 예정이다.


앞선 비동기 프로그래밍 주제와 연결하여, DOM 조작은 비동기 작업과 결합될 때 더욱 강력한 기능을 발휘한다. 콘솔 또는 HTML 파일에서 예제를 실행하며 DOM 조작의 실용성을 체감할 수 있다. 예제를 풍부하게 포함하여 실습 중심으로 진행한다.

DOM의 정의와 구조

DOM(Document Object Model)은 HTML 문서를 객체로 표현한 구조이다. 웹 페이지의 모든 요소(태그, 속성, 텍스트 등)를 자바스크립트로 접근하고 조작할 수 있는 인터페이스를 제공한다. 브라우저는 HTML을 파싱하여 DOM 트리라는 계층적 구조를 생성하며, 이 트리를 통해 요소 간 관계와 내용을 관리한다.


다음은 간단한 HTML 문서의 예이다:

<!DOCTYPE html>
<html>
<head>
    <title>DOM 예제</title>
</head>
<body>
    <h1 id="title">안녕하세요</h1>
    <p class="text">이것은 테스트 문단입니다.</p>
</body>
</html>
        

위 HTML은 브라우저에서 DOM 트리로 변환된다. html 요소가 루트 노드이며, 그 아래 headbody가 자식 노드로 존재한다. body 내부에는 h1p가 포함된다. 이러한 트리 구조는 부모-자식 관계로 연결되어 자바스크립트로 탐색과 수정이 가능하다.


DOM 조작은 이 트리의 노드를 선택하고 값을 변경하거나 새로운 노드를 추가하는 과정이다. 예를 들어, h1 요소의 텍스트를 변경하거나 새로운 p 요소를 삽입할 수 있다. 이 기술은 동적인 웹 애플리케이션 개발의 기반이다.


DOM 요소 선택 방법

DOM 조작을 시작하려면 HTML 요소를 선택해야 한다. 자바스크립트는 다양한 메서드를 제공하여 요소를 선택한다. 주요 메서드를 살펴본다.


1. getElementById

ID를 기준으로 단일 요소를 선택한다:

const title = document.getElementById('title');
console.log(title); // 

안녕하세요

ID는 HTML 문서에서 고유해야 하므로 하나의 요소만 반환된다. 해당 ID가 존재하지 않으면 null이 반환된다.


2. getElementsByClassName

클래스 이름을 기준으로 요소를 선택한다:

const texts = document.getElementsByClassName('text');
console.log(texts); // HTMLCollection [

이것은 테스트 문단입니다.

]

클래스는 여러 요소에 적용될 수 있으므로 HTMLCollection 형태로 반환된다. 이는 배열처럼 보이지만 반복문을 사용하여 접근한다.


3. querySelector

CSS 선택자를 사용하여 첫 번째 일치하는 요소를 선택한다:

const title = document.querySelector('#title');
console.log(title); // 

안녕하세요

const text = document.querySelector('.text'); console.log(text); //

이것은 테스트 문단입니다.

이 메서드는 CSS 문법을 활용하므로 직관적이다.


4. querySelectorAll

CSS 선택자와 일치하는 모든 요소를 선택한다:

const texts = document.querySelectorAll('.text');
console.log(texts); // NodeList [

이것은 테스트 문단입니다.

]

NodeList 형태로 반환되며, 반복문을 통해 각 요소를 처리한다.


예제: 여러 요소 선택

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>선택 예제</title>
</head>
<body>
    <p class="item">항목 1</p>
    <p class="item">항목 2</p>
    <p class="item">항목 3</p>
    <script>
        const items = document.querySelectorAll('.item');
        items.forEach(item => console.log(item.textContent));
        // 출력: "항목 1", "항목 2", "항목 3"
    </script>
</body>
</html>
        

이 예제는 querySelectorAllforEach를 결합하여 모든 항목의 텍스트를 출력한다.


DOM 속성과 스타일 수정

요소를 선택한 후에는 속성과 스타일을 수정한다. 이는 DOM 조작의 기본 과정이다.


1. 속성 수정

getAttributesetAttribute를 사용하여 속성을 조작한다:

const title = document.getElementById('title');
console.log(title.getAttribute('id')); // "title"
title.setAttribute('id', 'newTitle');
console.log(title.id); // "newTitle"
        

기본 속성은 직접 접근하여 수정할 수도 있다:

title.id = 'anotherTitle';
console.log(title.id); // "anotherTitle"
        

2. 텍스트 수정

textContentinnerHTML을 활용하여 요소의 텍스트를 변경한다:

title.textContent = '안녕!';
console.log(title.textContent); // "안녕!"

title.innerHTML = '안녕!';
console.log(title.innerHTML); // "안녕!"
        

textContent는 순수 텍스트를 수정하며, innerHTML은 HTML 태그를 포함한다.


3. 스타일 수정

style 속성을 사용하여 CSS를 조정한다:

title.style.color = 'blue';
title.style.fontSize = '24px';
        

스타일 속성은 CamelCase로 작성한다.

예제: 속성과 스타일 조작

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>속성/스타일 조작</title>
</head>
<body>
    <h1 id="header">제목</h1>
    <script>
        const header = document.getElementById('header');
        header.textContent = '변경된 제목';
        header.style.color = 'red';
        header.setAttribute('data-info', '테스트');
        console.log(header.getAttribute('data-info')); // "테스트"
    </script>
</body>
</html>
        

이 예제는 텍스트, 스타일, 커스텀 속성을 수정한다.


DOM 요소 생성과 삭제

DOM 조작은 요소를 생성하거나 삭제하는 기능도 포함한다. 이는 동적인 웹 콘텐츠 제작에 활용된다.

1. 요소 생성과 추가

createElementappendChild를 사용하여 새 요소를 추가한다:

const newP = document.createElement('p');
newP.textContent = '새로운 문단입니다.';
document.body.appendChild(newP);
        

2. 특정 위치에 삽입

insertBefore를 활용하여 요소를 특정 위치에 삽입한다:

const anotherP = document.createElement('p');
anotherP.textContent = '앞에 삽입된 문단입니다.';
document.body.insertBefore(anotherP, document.getElementById('title'));
        

3. 요소 삭제

removeChild를 사용하여 요소를 삭제한다:

document.body.removeChild(newP);
        

예제: 요소 생성과 삭제

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>요소 관리</title>
</head>
<body>
    <div id="container"></div>
    <button id="addBtn">추가</button>
    <button id="removeBtn">삭제</button>
    <script>
        const container = document.getElementById('container');
        const addBtn = document.getElementById('addBtn');
        const removeBtn = document.getElementById('removeBtn');
        let count = 0;

        addBtn.addEventListener('click', () => {
            const div = document.createElement('div');
            div.textContent = `항목 ${++count}`;
            container.appendChild(div);
        });

        removeBtn.addEventListener('click', () => {
            const lastChild = container.lastElementChild;
            if (lastChild) container.removeChild(lastChild);
        });
    </script>
</body>
</html>
        

이 예제는 버튼 클릭으로 요소를 추가하고 삭제한다.


DOM 조작과 이벤트 결합

DOM 조작은 이벤트와 결합하면 상호작용성을 높인다. 다음은 이벤트로 DOM을 조작하는 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>이벤트와 DOM</title>
    <style>
        .highlight {
            background-color: yellow;
        }
    </style>
</head>
<body>
    <p id="text">클릭하세요!</p>
    <script>
        const text = document.getElementById('text');
        text.addEventListener('click', () => {
            text.classList.toggle('highlight');
        });
    </script>
</body>
</html>
        

이 예제는 클릭 시 요소에 클래스를 추가하거나 제거한다.


예제: 버튼으로 색상 변경

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>색상 변경</title>
</head>
<body>
    <button id="colorBtn">색상 변경</button>
    <div id="box" style="width: 100px; height: 100px; background-color: gray;"></div>
    <script>
        const colorBtn = document.getElementById('colorBtn');
        const box = document.getElementById('box');
        const colors = ['red', 'blue', 'green'];
        let index = 0;

        colorBtn.addEventListener('click', () => {
            box.style.backgroundColor = colors[index];
            index = (index + 1) % colors.length;
        });
    </script>
</body>
</html>
        

이 예제는 버튼 클릭으로 박스의 색상을 순환한다.


실습 예제: 동적 리스트 관리

다음은 DOM 조작을 활용한 동적 리스트 관리 예제이다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>동적 리스트</title>
    <style>
        #list {
            list-style-type: none;
            padding: 0;
        }
        #list li {
            padding: 10px;
            background: #f0f0f0;
            margin: 5px 0;
            display: flex;
            justify-content: space-between;
        }
        button {
            margin: 5px;
        }
    </style>
</head>
<body>
    <input type="text" id="input" placeholder="항목 입력">
    <button id="addBtn">추가</button>
    <ul id="list"></ul>
    <script>
        const input = document.getElementById('input');
        const addBtn = document.getElementById('addBtn');
        const list = document.getElementById('list');

        addBtn.addEventListener('click', () => {
            if (input.value) {
                const li = document.createElement('li');
                li.textContent = input.value;
                const deleteBtn = document.createElement('button');
                deleteBtn.textContent = '삭제';
                deleteBtn.addEventListener('click', () => list.removeChild(li));
                li.appendChild(deleteBtn);
                list.appendChild(li);
                input.value = '';
            }
        });
    </script>
</body>
</html>
        

이 예제는 입력값으로 리스트 항목을 추가하고, 각 항목에 삭제 버튼을 포함한다.

DOM Manipulation Example

DOM 조작의 실무 활용

DOM 조작은 실무에서 사용자 인터페이스를 동적으로 업데이트하는 데 사용된다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>실시간 데이터</title>
</head>
<body>
    <button id="fetchBtn">데이터 가져오기</button>
    <div id="result"></div>
    <script>
        const fetchBtn = document.getElementById('fetchBtn');
        const result = document.getElementById('result');

        fetchBtn.addEventListener('click', async () => {
            result.textContent = '로딩 중...';
            const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
            const data = await response.json();
            result.textContent = `제목: ${data.title}`;
        });
    </script>
</body>
</html>
        </code></pre>
        <p>이 예제는 비동기 데이터를 가져와 DOM에 표시한다.</p>
        <p><b>예제: 실시간 입력 반영</b></p>
        <pre><code><!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>입력 반영</title>
</head>
<body>
    <input type="text" id="input" placeholder="텍스트 입력">
    <p id="output"></p>
    <script>
        const input = document.getElementById('input');
        const output = document.getElementById('output');

        input.addEventListener('input', () => {
            output.textContent = input.value;
        });
    </script>
</body>
</html>
        

이 예제는 입력값을 실시간으로 반영한다.


주의할 점

DOM 선택 시 ID와 클래스가 정확해야 한다. innerHTML 사용 시 보안 취약점(XSS)을 주의한다. 과도한 DOM 조작은 성능 저하를 유발할 수 있다.


결론

DOM의 개념, 요소 선택, 속성과 스타일 수정, 생성과 삭제를 학습하며 실습 예제를 통해 실용성을 확인하였다.

+ Recent posts