자바스크립트 이벤트 루프와 비동기 고급 활용

자바스크립트 이벤트 루프와 비동기 고급 활용

지난 8번째 글에서 비동기 기초를 다루며 콜백, 프로미스, async/await를 학습하였다면, 이번에는 비동기의 핵심 메커니즘인 이벤트 루프(Event Loop)를 탐구한다. 또한 이벤트 루프를 기반으로 비동기를 심화하여 활용하는 고급 기술을 소개한다. 이벤트 루프는 자바스크립트의 단일 스레드 실행 방식을 이해하는 데 필수적인 개념이며, 비동기 코드를 효율적으로 관리하는 데 중요한 역할을 한다.


이벤트 루프의 동작 원리와 태스크 큐, 마이크로태스크 큐의 차이를 설명하며, 비동기 패턴의 고급 응용과 실습 예제를 다룬다. 콘솔이나 HTML 파일에서 예제를 실행하며 비동기의 동작 방식을 체감할 수 있다.


이전 비동기 기초 내용을 확장하여 자바스크립트 엔진이 비동기 작업을 처리하는 방식을 심도 있게 분석한다. 이벤트 루프를 이해하면 코드 실행 순서를 예측하고 복잡한 비동기 상황을 효과적으로 다룰 수 있다.

Event Loop Overview

이벤트 루프의 정의

이벤트 루프(Event Loop)는 자바스크립트가 비동기 작업을 처리하는 핵심 메커니즘이다. 자바스크립트는 단일 스레드(Single-Threaded) 언어로, 한 번에 하나의 작업만 수행한다. 그러나 setTimeout이나 fetch와 같은 비동기 작업은 동시에 처리되는 것처럼 보인다. 이는 이벤트 루프가 비동기 작업을 관리하기 때문이다.


자바스크립트 엔진(예: V8)에는 호출 스택(Call Stack)이 존재하며, 코드는 순차적으로 이 스택에 쌓여 실행된다. 비동기 작업은 호출 스택 외부에서 처리되며, 이벤트 루프가 이를 호출 스택으로 다시 가져오는 역할을 수행한다. 다음 예제를 통해 확인한다:

console.log('시작');
setTimeout(() => {
    console.log('타이머 끝');
}, 1000);
console.log('끝');
// 출력: "시작" -> "끝" -> 1초 후 "타이머 끝"
        

위 코드에서 setTimeout은 즉시 실행되지 않고 1초 후에 출력된다. 이는 이벤트 루프가 비동기 작업을 별도로 처리하여 호출 스택으로 적절히 전달하기 때문이다.


이벤트 루프는 호출 스택이 비었을 때 태스크 큐(Task Queue)에서 대기 중인 작업을 가져와 실행한다. 이 과정은 브라우저나 Node.js와 같은 런타임 환경이 제공하는 Web API와 협력하여 이루어진다. 예를 들어, setTimeout은 Web API에서 타이머를 처리한 후, 완료 시 태스크 큐에 콜백을 추가한다. 이벤트 루프는 이를 확인하고 호출 스택으로 이동시켜 실행한다.


이벤트 루프의 동작 과정

이벤트 루프의 동작 과정을 단계별로 분석한다. 이를 이해하면 비동기 코드의 실행 순서를 명확히 파악할 수 있다.


1. 호출 스택(Call Stack)

호출 스택은 자바스크립트가 코드를 실행하는 공간이다. 함수가 호출되면 스택에 추가되고, 실행이 완료되면 제거된다:

function a() {
    console.log('A');
}
function b() {
    a();
    console.log('B');
}
b();
// 출력: "A" -> "B"
        

2. Web API

비동기 작업(예: setTimeout, fetch)은 Web API로 전달된다. Web API는 브라우저가 제공하며, 자바스크립트 엔진 외부에서 작업을 처리한다:

setTimeout(() => {
    console.log('타이머');
}, 0);
console.log('즉시');
// 출력: "즉시" -> "타이머"
        

위 예제에서 setTimeout의 지연 시간이 0초임에도 즉시 실행되지 않는다. 이는 Web API에서 처리된 후 태스크 큐로 이동하기 때문이다.


3. 태스크 큐와 마이크로태스크 큐

비동기 작업이 완료되면 콜백이 큐에 저장된다. 큐에는 두 가지 종류가 존재한다:


- 태스크 큐(Task Queue): setTimeout, setInterval과 같은 일반 비동기 작업이 저장된다.

- 마이크로태스크 큐(Microtask Queue): Promise, queueMicrotask와 같은 우선순위가 높은 작업이 저장된다.

console.log('시작');
setTimeout(() => console.log('타이머'), 0);
Promise.resolve().then(() => console.log('프로미스'));
console.log('끝');
// 출력: "시작" -> "끝" -> "프로미스" -> "타이머"
        

마이크로태스크 큐의 작업은 태스크 큐보다 먼저 실행된다. 이는 우선순위의 차이 때문이다.


4. 이벤트 루프의 실행

이벤트 루프는 호출 스택이 비었을 때 마이크로태스크 큐의 모든 작업을 먼저 처리한 후, 태스크 큐의 작업을 처리한다. 이 과정은 지속적으로 반복된다:

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
setTimeout(() => console.log('4'), 0);
console.log('5');
// 출력: "1" -> "5" -> "3" -> "2" -> "4"
        
Event Loop Process

비동기 패턴의 고급 활용

이벤트 루프를 이해한 후에는 비동기를 더욱 효과적으로 활용하는 방법을 탐구한다. 몇 가지 고급 패턴을 소개한다.


1. 프로미스 체이닝 개선

프로미스를 연속적으로 연결하여 코드를 깔끔하게 관리한다:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
delay(1000)
    .then(() => {
        console.log('1초 후');
        return delay(500);
    })
    .then(() => console.log('1.5초 후'));
        

이 방법은 콜백 중첩 문제를 해결하며 가독성을 높인다.


2. 병렬 프로미스 처리

Promise.all을 활용하여 여러 비동기 작업을 병렬로 실행한다:

const p1 = delay(1000).then(() => '작업 1');
const p2 = delay(2000).then(() => '작업 2');
Promise.all([p1, p2]).then(results => {
    console.log(results); // ["작업 1", "작업 2"]
});
        

이 패턴은 긴 작업 시간을 단축한다.


3. 비동기 에러 처리 강화

async/await를 사용하여 에러를 체계적으로 처리한다:

async function fetchData() {
    try {
        const result = await Promise.reject('에러!');
    } catch (error) {
        console.log('잡힌 에러:', error);
    }
}
fetchData(); // "잡힌 에러: 에러!"
        

4. 순차 실행 제어

비동기 작업을 순차적으로 실행한다:

async function sequentialTasks() {
    await delay(1000);
    console.log('1초 후');
    await delay(500);
    console.log('1.5초 후');
}
sequentialTasks();
        

실습 예제

이벤트 루프와 비동기를 활용한 실습 예제를 제공한다. 버튼을 통해 순차적 및 병렬 작업을 시뮬레이션한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>비동기 실습</title>
    <style>
        #result {
            margin-top: 20px;
        }
        button {
            margin: 5px;
        }
    </style>
</head>
<body>
    <button id="sequentialBtn">순차 실행</button>
    <button id="parallelBtn">병렬 실행</button>
    <div id="result"></div>
    <script>
        function delay(ms) {
            return new Promise(resolve => setTimeout(() => resolve(ms), ms));
        }

        const sequentialBtn = document.getElementById('sequentialBtn');
        const parallelBtn = document.getElementById('parallelBtn');
        const result = document.getElementById('result');

        sequentialBtn.addEventListener('click', async () => {
            result.textContent = '순차 실행 시작...';
            await delay(1000);
            result.textContent += '\n1초 후';
            await delay(500);
            result.textContent += '\n1.5초 후';
        });

        parallelBtn.addEventListener('click', async () => {
            result.textContent = '병렬 실행 시작...';
            const tasks = [delay(1000), delay(500)];
            const times = await Promise.all(tasks);
            result.textContent += `\n${times.join(', ')}ms 후 완료`;
        });
    </script>
</body>
</html>
비동기 실습

이 예제는 순차 실행과 병렬 실행의 차이를 시각적으로 확인할 수 있다.

Async Example Screen

비동기와 이벤트 루프의 실무 활용

실무에서는 API 호출, 데이터 로딩, 애니메이션 등 다양한 상황에서 이벤트 루프와 비동기를 활용한다:

async function loadData() {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
    const data = await response.json();
    console.log(data);
}
loadData();
        

이 예시는 API 데이터를 비동기로 가져오는 일반적인 사례이다.


주의할 점

마이크로태스크와 태스크의 우선순위를 정확히 이해한다. 비동기 작업이 많아질 경우 실행 순서를 예측한다. 에러 처리는 필수적으로 포함한다.


결론

이벤트 루프의 동작 과정, 큐의 차이, 고급 패턴을 학습함으로써 비동기 코드를 효과적으로 다룰 수 있다. 다음 포스팅에서는 뭐하지..

자바스크립트로 만드는 실시간 채팅 앱 (Part 3)

자바스크립트로 만드는 실시간 채팅 앱 (Part 3)

Part 2에서 Socket.IO를 활용하여 실시간 메시지 송수신 기능을 구현하였다면, 이번 Part 3에서는 채팅 UI를 개선하고 배포 방법을 다루려고 한다. 이 과정을 통해 채팅 앱을 제작하고 공유할 수 있다.


UI 개선과 배포는 채팅 앱을 실용적으로 만드는 중요한 단계이다. Part 3에서는 메시지 스타일을 개선하고 사용자 이름을 추가하며, 무료 배포 플랫폼을 활용하여 앱을 온라인에 공개한다. 자


UI를 개선하고 배포를 실습하며, 자바스크립트의 비동기 처리와 이벤트 핸들링이 실무에서 어떻게 활용되는지 알아본다. 차분히 진행하며 실습을 시작한다.

UI Improvement and Deployment Overview

채팅 UI 개선

Part 2의 UI를 개선하려고 한다. 사용자 이름 입력 필드를 추가하고 메시지 스타일을 조정한다. index.html 파일을 다음 코드로 수정한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>간단한 채팅 앱</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f0f0f0;
        }
        #chat-container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        #messages {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            padding: 10px;
            margin-bottom: 10px;
        }
        #messages p {
            margin: 5px 0;
            padding: 8px;
            background: #e9ecef;
            border-radius: 3px;
        }
        #message-form {
            display: flex;
            gap: 10px;
        }
        #username-input, #message-input {
            padding: 5px;
        }
        #username-input {
            width: 100px;
        }
        #message-input {
            flex-grow: 1;
        }
        button {
            padding: 5px 10px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div id="chat-container">
        <div id="messages"></div>
        <form id="message-form">
            <input type="text" id="username-input" placeholder="이름">
            <input type="text" id="message-input" placeholder="메시지를 입력하세요">
            <button type="submit">보내기</button>
        </form>
    </div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io();
        const form = document.getElementById('message-form');
        const usernameInput = document.getElementById('username-input');
        const messageInput = document.getElementById('message-input');
        const messages = document.getElementById('messages');

        socket.on('connect', () => {
            console.log('서버에 연결됐습니다!');
        });

        socket.on('chat message', (data) => {
            const item = document.createElement('p');
            item.textContent = `${data.username}: ${data.message}`;
            messages.appendChild(item);
            messages.scrollTop = messages.scrollHeight;
        });

        form.addEventListener('submit', (e) => {
            e.preventDefault();
            if (usernameInput.value && messageInput.value) {
                const data = {
                    username: usernameInput.value,
                    message: messageInput.value
                };
                socket.emit('chat message', data);
                messageInput.value = '';
            }
        });
    </script>
</body>
</html>
간단한 채팅 앱

사용자 이름 입력 필드가 추가되었으며, 메시지는 p 태그로 스타일링된다. scrollTop 속성은 새 메시지가 표시될 때 자동으로 스크롤을 조정한다. 이는 사용자 경험을 개선하는 요소이다.

CSS는 메시지에 배경색과 둥근 테두리를 적용하여 채팅 앱의 시각적 요소를 강화한다. 간단한 스타일링을 통해 UI를 향상시킬 수 있다.

Enhanced UI

서버 코드 수정

클라이언트에서 사용자 이름과 메시지를 객체로 전송하므로, 서버에서도 이를 처리할 수 있도록 수정한다. server.js 파일을 다음 코드로 업데이트한다:

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
    console.log('사용자가 연결됐습니다!');
    socket.on('chat message', (data) => {
        io.emit('chat message', data);
    });
    socket.on('disconnect', () => {
        console.log('사용자가 연결을 끊었습니다.');
    });
});

http.listen(3000, () => {
    console.log('서버가 3000번 포트에서 실행 중입니다.');
});
        

data 객체를 수신하여 모든 클라이언트에 브로드캐스트한다. 서버를 재시작(Ctrl+Cnode server.js)하여 변경 사항을 적용한다.

이 수정은 사용자 이름과 메시지를 함께 표시하며, 실시간 채팅의 기능을 향상시킨다. 객체 데이터 전송 과정을 실습하며 이해할 수 있다.


배포를 위한 준비

로컬 환경에서 완성된 채팅 앱을 온라인에 배포하려고 한다. 무료 플랫폼인 Render를 활용하여 배포한다.

1. GitHub 업로드

프로젝트를 GitHub에 업로드한다. chat-app 폴더에서 터미널을 열고 다음 명령어를 실행한다:

git init
git add .
git commit -m "Initial chat app commit"
git remote add origin [GitHub Repository URL]
git push -u origin main
        

[GitHub Repository URL]은 GitHub에서 생성한 새 리포지토리의 URL로 대체한다. Git 명령어는 낯설 수 있으나, 단계별로 진행하면 어렵지 않다.


2. Render 배포

Render 사이트에서 GitHub 계정으로 로그인하고, "New" -> "Web Service"를 선택한다. 아래 설정을 적용한다:

- Runtime: Node

- Start Command: node server.js

무료 플랜으로 "Create Web Service"를 클릭하여 배포를 시작한다. 배포 완료 후 제공된 URL을 통해 접속할 수 있다.


배포 결과 확인

Render에서 제공된 URL을 브라우저에서 열어 확인한다. 다른 사용자와 공유하여 실시간으로 메시지를 주고받을 수 있다. 이는 자바스크립트로 제작한 애플리케이션을 배포한 결과이다.

현재 앱은 기본 기능만 포함하며, 사용자 접속 알림이나 메시지 삭제 기능 같은 추가 개선이 가능하다. 이 단계를 통해 배포 경험을 쌓을 수 있다.

Deployed Chat App

주의할 점

무료 배포 플랫폼은 트래픽 제한이 있다. 코드 수정 후에는 GitHub에 푸시하고 Render에서 재배포한다. 보안은 고려되지 않았으므로 실무에서는 추가 조치가 필요하다.

결론

Part 3에서는 채팅 UI를 개선하고 Render를 통해 배포를 완료하였다. 자바스크립트를 활용한 실시간 채팅 앱을 제작하고 온라인에 공개함으로써, 비동기와 이벤트 처리의 실용성을 확인하였다. 이 시리즈는 초보자를 위한 실습 중심 가이드이다.

자바스크립트로 만드는 실시간 채팅 앱 - (Part 2)

자바스크립트로 만드는 실시간 채팅 앱 (Part 2)

Part 1에서 Node.js 환경과 기본 HTML/CSS 구조를 준비하였다면, 이번 Part 2에서는 Socket.IO를 활용하여 클라이언트와 서버를 연결하고 실시간 메시지 송수신 기능을 구현하려고 한다. 실시간 통신은 채팅 앱의 핵심이며, 초보자도 이해할 수 있도록 상세히 설명한다.


Socket.IO를 처음 사용하는 초보자에게는 실시간 통신이 신기하게 느껴질 수 있다. 그러나 이 라이브러리는 복잡한 웹소켓 설정을 간소화하여 몇 줄의 코드로 실시간 기능을 구현할 수 있도록 지원한다. 이번 포스팅에서는 서버와 클라이언트를 연결하고 메시지를 주고받는 과정을 실습하며, 자바스크립트의 비동기 처리와 이벤트 핸들링이 실시간 애플리케이션에서 어떻게 활용되는지 알아본다.


Part 1에서 준비한 파일을 활용하여 실시간 통신을 구현하며, 초보자도 브라우저에서 직접 결과를 확인할 수 있도록 안내한다. 차분히 진행하며 실습을 시작한다.

Realtime Communication Overview

Socket.IO로 서버 설정

Part 1에서 작성한 server.js 파일을 수정하여 Socket.IO를 추가한다. 이 라이브러리는 실시간 통신을 가능하게 하며, 다음 코드를 server.js에 적용한다:

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
    console.log('사용자가 연결됐습니다!');
    socket.on('disconnect', () => {
        console.log('사용자가 연결을 끊었습니다.');
    });
});

http.listen(3000, () => {
    console.log('서버가 3000번 포트에서 실행 중입니다.');
});
        

io.on('connection')은 클라이언트가 서버에 접속할 때마다 실행된다. socket 객체는 각 클라이언트 연결을 관리하며, 접속과 연결 해제를 감지한다. 터미널에서 node server.js를 실행하고 브라우저에서 http://localhost:3000을 열면, 터미널에 "사용자가 연결됐습니다!" 메시지가 출력된다.

Socket.IO는 웹소켓을 기반으로 하여 실시간 양방향 통신을 지원한다. 초보자에게는 복잡한 개념일 수 있으나, 이 라이브러리를 통해 쉽게 실습할 수 있다. 서버 설정은 실시간 통신의 기초이며, 클라이언트 연결 준비를 완료한다.

Server Setup Screen

클라이언트에서 Socket.IO 연결

서버 설정이 완료되었으므로, 클라이언트에서 서버와 연결하고 메시지 송신을 준비한다. index.html 파일을 다음 코드로 수정한다:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>간단한 채팅 앱</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f0f0f0;
        }
        #chat-container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        #messages {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            padding: 10px;
            margin-bottom: 10px;
        }
        #message-form {
            display: flex;
            gap: 10px;
        }
        #message-input {
            flex-grow: 1;
            padding: 5px;
        }
        button {
            padding: 5px 10px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div id="chat-container">
        <div id="messages"></div>
        <form id="message-form">
            <input type="text" id="message-input" placeholder="메시지를 입력하세요">
            <button type="submit">보내기</button>
        </form>
    </div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io();
        const form = document.getElementById('message-form');
        const input = document.getElementById('message-input');

        socket.on('connect', () => {
            console.log('서버에 연결됐습니다!');
        });

        form.addEventListener('submit', (e) => {
            e.preventDefault();
            if (input.value) {
                socket.emit('chat message', input.value);
                input.value = '';
            }
        });
    </script>
</body>
</html>

간단한 채팅 앱

socket.on('chat message')는 서버로부터 메시지를 수신할 때 실행된다. 수신된 메시지는 p 태그로 생성되어 messages 요소에 추가된다. 이 과정은 실시간 채팅의 기본 동작을 구현한다.

브라우저에서 http://localhost:3000을 여러 창으로 열어 테스트한다. 한 창에서 메시지를 입력하면 다른 창에도 즉시 표시된다. 이는 실시간 통신의 결과이다.


현재 진행 상황 점검

여기까지 완료하면 서버와 클라이언트가 연결되어 실시간으로 메시지를 주고받을 수 있다. 터미널에서 node server.js를 실행하고, 브라우저에서 http://localhost:3000을 여러 창으로 열어 확인한다. 메시지가 모든 창에 실시간으로 표시된다면 성공이다.

현재 UI는 단순하며, 사용자 이름 표시와 같은 기능은 아직 포함되지 않았다. Part 3에서는 UI를 개선하고 배포를 진행한다. 약 5,000자의 분량으로 상세히 다루었으며, 초보자도 실습을 통해 실시간 통신을 이해할 수 있다.

이 단계는 자바스크립트로 실시간 채팅 앱을 구현하는 핵심 과정을 완료한다. 다음 포스팅에서는 UI를 개선하여 사용자 경험을 향상시킨다.

Message Receive Screen

주의할 점

Socket.IO 버전은 서버와 클라이언트에서 일치해야 한다. 브라우저 캐시로 인해 최신 코드가 반영되지 않을 수 있으므로 새로고침한다. 서버가 종료되면 통신이 중단되므로 실행 상태를 유지한다.


결론

Part 2에서는 Socket.IO를 활용하여 클라이언트와 서버를 연결하고 실시간 메시지 송수신 기능을 구현하였다. 자바스크립트의 비동기 처리와 이벤트 핸들링이 실시간 통신에 적용되는 과정을 실습하였다. Part 3에서는 UI를 개선하고 배포 방법을 다룬다.

자바스크립트로 만드는 실시간 채팅 앱 - 초보자 가이드 (Part 1)

자바스크립트로 만드는 실시간 채팅 앱 (Part 1)

이번 포스팅은 자바스크립트를 활용하여 실시간 채팅 앱을 만드는 과정을 다루려고 한다. 총 3부작으로 구성되며, Part 1에서는 기본 설정과 HTML/CSS 구조를 준비하려고 한다. Part 2에서는 클라이언트와 서버를 연결하고, Part 3에서는 UI를 개선한 후 배포 방법을 소개한다.


실시간 채팅 앱을 처음 접하는 초보자라면 시작점이 모호하게 느껴질 수 있다. 그러나 이 가이드는 단계별로 진행되며, 자바스크립트의 비동기 처리와 이벤트 핸들링을 실습할 수 있는 실용적인 예제를 제공한다. Socket.IO라는 라이브러리를 사용하여 실시간 통신을 구현하며, 이는 복잡한 웹소켓 개념을 간소화한 도구이다. 이 과정을 통해 자바스크립트를 활용한 동적인 웹 애플리케이션 제작 방법을 익힐 수 있다.


이 포스팅 시리즈는 자바스크립트의 기본 지식을 바탕으로 실질적인 프로젝트를 완성하는 데 초점을 맞춘다. Part 1에서는 환경 설정과 기본 UI를 구성하며, 쉽게 따라 할 수 있도록 준비했으니, 차분히 진행하며 실습을 시작해보자.


Chat App Overview

채팅 앱의 개념

이번에 제작하려는 실시간 채팅 앱은 사용자가 입력한 메시지를 다른 사용자가 즉시 확인할 수 있는 애플리케이션이다. 예를 들어, 카카오톡이나 슬랙과 유사한 기능을 갖추고 있다. 다만, 이 가이드에서는 복잡한 기능을 배제하고, 메시지 송수신의 기본 기능에 집중한다. 자바스크립트를 통해 클라이언트와 서버 간 실시간 통신을 구현하며, Socket.IO를 활용하여 이를 간단히 처리한다.


실시간 통신은 웹소켓 기술을 기반으로 한다. 초보자에게 웹소켓은 다소 생소할 수 있으나, Socket.IO는 이를 추상화하여 간편하게 사용할 수 있도록 설계되었다. 이 과정을 통해 자바스크립트의 비동기 처리와 이벤트 핸들링이 실제 프로젝트에서 어떻게 적용되는지 이해할 수 있다. 실무에서도 채팅 기능은 다양한 웹 애플리케이션에서 사용되므로, 이번 학습은 향후 활용도가 높다.


채팅 앱 제작을 위해서는 환경 설정부터 시작해야 한다. Node.js를 사용해 서버를 구성하고, HTML과 CSS로 기본적인 사용자 인터페이스를 준비한다. 이 과정은 자바스크립트 기본 지식을 점검하며, 실시간 통신 기술을 익히는 데 초점을 맞춘다. 초보자도 단계별로 진행하면 충분히 완성할 수 있으니, 차분히 따라오길 바란다.


Realtime Communication Intro

필요한 도구와 준비

이 채팅 앱을 제작하려면 몇 가지 도구가 필요하다. 초보자도 최소한의 설정으로 시작할 수 있도록 간소화된 준비 단계를 제시한다.


1. Node.js

Node.js는 자바스크립트를 브라우저 외부에서 실행할 수 있는 런타임 환경이다. 서버를 구성하는 데 사용된다. 공식 사이트(nodejs.org)에서 최신 LTS 버전을 다운로드하여 설치한다. 설치 후 터미널에서 다음 명령어를 실행하여 확인한다:

node -v
npm -v

node -v는 Node.js 버전을 확인하며, npm -v는 패키지 매니저의 버전을 표시한다. LTS 버전은 안정적이며, 대부분의 경우 문제가 발생하지 않는다.


2. 텍스트 에디터

코드를 작성하려면 텍스트 에디터가 필요하다. VS Code는 무료이며 자바스크립트 코딩에 적합한 기능을 제공한다. 공식 사이트(code.visualstudio.com)에서 다운로드하여 설치한다.


3. 브라우저

채팅 앱을 테스트하려면 웹 브라우저가 필요하다. Chrome 또는 Firefox는 개발자 도구(F12)를 통해 콘솔 확인이 용이하므로 적합하다.

이 세 가지 도구만 준비되면 충분하다. 설치 과정은 약 10분 이내에 완료되며, 초보자도 어렵지 않게 설정할 수 있다.


프로젝트 환경 구성

도구 준비가 완료되었다면, 프로젝트 환경을 구성한다. 서버와 클라이언트 파일을 설정하여 채팅 앱의 기반을 마련한다.


1. 프로젝트 폴더 생성

원하는 경로에 chat-app라는 폴더를 생성한다. 터미널을 열고 다음 명령어로 이동한다:

mkdir chat-app
cd chat-app

경로가 정확히 설정되었는지 확인한다.


2. npm 초기화

프로젝트 설정 파일을 생성하려면 npm을 초기화한다. 터미널에서 다음 명령어를 실행한다:

npm init -y

-y 옵션은 기본 설정을 적용하며, package.json 파일이 생성된다. 이 파일은 프로젝트 정보와 의존성을 관리한다.


3. 패키지 설치

서버를 위한 Express와 실시간 통신을 위한 Socket.IO를 설치한다. 다음 명령어를 실행한다:

npm install express socket.io

설치가 완료되면 node_modules 폴더와 package-lock.json 파일이 생성된다. 이는 정상적인 설치 과정이다.


HTML과 CSS로 기본 UI 설계

환경 구성이 완료되었으므로, 채팅 앱의 기본 사용자 인터페이스를 설계한다. HTML과 CSS를 통해 클라이언트 UI를 준비한다.


1. index.html 작성

chat-app 폴더에 index.html 파일을 생성하고 다음 코드를 입력한다:

 

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>간단한 채팅 앱</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 20px;
            background-color: #f0f0f0;
        }
        #chat-container {
            max-width: 600px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        #messages {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
            padding: 10px;
            margin-bottom: 10px;
        }
        #message-form {
            display: flex;
            gap: 10px;
        }
        #message-input {
            flex-grow: 1;
            padding: 5px;
        }
        button {
            padding: 5px 10px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div id="chat-container">
        <div id="messages"></div>
        <form id="message-form">
            <input type="text" id="message-input" placeholder="메시지를 입력하세요">
            <button type="submit">보내기</button>
        </form>
    </div>
</body>
</html>

이 코드는 메시지 표시 영역과 입력 폼을 구성한다. CSS는 간단한 박스 스타일과 버튼 디자인만 포함하며, 초보자도 이해하기 쉽게 최소화하였다.


2. 서버 파일 작성

chat-app 폴더에 server.js 파일을 생성하고 다음 코드를 입력한다:

const express = require('express');
const app = express();
const http = require('http').createServer(app);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

http.listen(3000, () => {
    console.log('서버가 3000번 포트에서 실행 중입니다.');
});
        

이 코드는 Express를 활용한 기본 서버를 설정하며, index.html을 제공한다. 터미널에서 다음 명령어를 실행한다:

node server.js
        

브라우저에서 http://localhost:3000을 열어 UI를 확인할 수 있다.

Basic UI Design

현재 진행 상황 점검

여기까지 진행하면 터미널에서 node server.js를 실행하여 서버를起動하고, 브라우저에서 http://localhost:3000을 열어 채팅 UI를 확인할 수 있다. 메시지 송수신 기능은 아직 구현되지 않았으나, 기본 구조는 완성되었다.

이 단계에서는 환경 설정과 UI 준비에 중점을 두었다. Part 2에서는 Socket.IO를 활용하여 실시간 통신을 구현한다. 약 5,000자의 분량으로 상세히 다루었으며, 초보자도 실습을 통해 충분히 완성할 수 있다.

이제 자바스크립트로 실시간 채팅 앱을 만드는 첫 단계를 완료하였다. 다음 포스팅에서는 클라이언트와 서버 간 연결을 설정하여 메시지 송수신 기능을 추가한다.

주의할 점

Node.js 버전이 낮으면 패키지 설치에 문제가 발생할 수 있다. LTS 버전을 사용한다. 터미널 경로가 정확해야 하며, 서버 실행 중 포트 충돌을 방지한다.

결론

Part 1에서는 자바스크립트를 활용한 실시간 채팅 앱 제작의 기본 환경과 UI를 구성하였다. 따라 할 수 있도록 Node.js 설치부터 HTML/CSS 설계까지 상세히 다루었다. Part 2에서는 Socket.IO를 통해 실시간 통신을 구현하려고 한다.

자바스크립트 비동기 기초 - JavaScript 시리즈 #8

자바스크립트 비동기 기초

지금까지 변수와 데이터 타입, 조건문과 반복문, 함수, 배열, 객체, 이벤트를 배웠다면, 이번엔 비동기 프로그래밍을 배워볼 거다. 비동기는 자바스크립트에서 시간이 걸리는 작업(예: 서버 요청, 파일 읽기)을 기다리지 않고 동시에 처리할 수 있게 해주는 핵심 개념으로, 현대 웹 개발에서 필수적이다. 비동기를 잘 이해하면 느린 작업에도 반응 빠른 애플리케이션을 만들 수 있다.


내가 처음 코딩을 배울 때 서버에서 데이터를 가져오는데 페이지가 멈추는 경험을 했는데, 비동기를 배우고 나니까 이런 문제를 해결할 수 있었다. 이번 글에선 비동기의 정의와 필요성, 콜백 함수, 프로미스, async/await 사용법, 그리고 실습 예제를 상세히 다룰 거다. 천천히 따라오면서 콘솔이나 HTML 파일에서 실행해보면 비동기의 매력을 느낄 수 있을 거다. 비동기는 자바스크립트가 단일 스레드 언어임에도 불구하고 멀티태스킹처럼 동작하게 해주는 강력한 도구다.


앞서 배운 개념들과 비동기를 결합하면 더 실용적인 코드를 작성할 수 있다. 예를 들어, 이벤트로 버튼 클릭을 감지하고, 비동기로 서버에서 데이터를 가져와서 객체에 저장한 뒤, 배열로 화면에 표시하는 작업을 쉽게 할 수 있다.

Async Concept

비동기란 무엇인가?

비동기(Asynchronous)는 코드가 순차적으로 실행되지 않고, 시간이 걸리는 작업을 기다리지 않고 다음 작업을 먼저 처리하는 방식이다. 자바스크립트는 기본적으로 단일 스레드(Single-Threaded) 언어로, 한 번에 한 가지 작업만 처리한다. 예를 들어, 서버에서 데이터를 가져오는 데 5초가 걸린다면 동기(Synchronous) 방식으론 그 5초 동안 아무것도 못 하고 기다려야 한다. 하지만 비동기 방식은 데이터를 요청한 뒤 기다리는 동안 다른 코드를 실행할 수 있다.

예를 들어, 동기 방식은 이렇게 동작한다:

console.log("시작");
function slowTask() {
    let start = Date.now();
    while (Date.now() - start < 3000) {} // 3초 대기
    console.log("느린 작업 완료");
}
slowTask();
console.log("끝");
// 출력: "시작" -> 3초 후 -> "느린 작업 완료" -> "끝"
        

위 코드는 3초 동안 멈춘 뒤 다음 줄로 넘어간다. 내가 처음 이걸 봤을 때 답답했는데, 비동기는 이런 문제를 해결한다:

console.log("시작");
setTimeout(function() {
    console.log("느린 작업 완료");
}, 3000);
console.log("끝");
// 출력: "시작" -> "끝" -> 3초 후 -> "느린 작업 완료"
        

setTimeout은 비동기 함수로, 3초 후에 실행할 코드를 예약하고 바로 다음 줄로 넘어간다. 내가 처음 비동기를 접했을 때 코드가 순서대로 안 돼서 혼란스러웠지만, 나중에 보니 이게 자바스크립트의 강점이었다.

비동기는 서버 요청, 파일 읽기, 타이머 같은 시간이 걸리는 작업에서 필수다. 실무에선 사용자 인터페이스가 멈추지 않게 하고, 데이터를 백그라운드에서 가져오거나, 여러 작업을 병렬로 처리할 때 많이 쓴다. 자바스크립트의 비동기는 이벤트 루프(Event Loop)라는 메커니즘으로 동작하는데, 이는 나중에 더 깊이 다룰 예정이다.

Async Flow

콜백 함수로 비동기 처리

비동기를 처리하는 가장 기본적인 방법은 콜백 함수(Callback Function)다. 콜백은 작업이 끝난 후 실행할 함수를 전달하는 방식이다.

기본 예제

console.log("시작");
setTimeout(function() {
    console.log("3초 후 실행");
}, 3000);
console.log("끝");
// 출력: "시작" -> "끝" -> 3초 후 -> "3초 후 실행"
        

setTimeout에 전달된 함수가 콜백이다. 내가 처음 콜백을 썼을 때 타이밍이 신기했는데, 비동기의 기본을 이해하는 데 큰 도움이 됐다.

중첩 콜백 문제

콜백을 여러 번 쓰면 코드가 복잡해진다(Callback Hell):

setTimeout(function() {
    console.log("첫 번째 작업");
    setTimeout(function() {
        console.log("두 번째 작업");
        setTimeout(function() {
            console.log("세 번째 작업");
        }, 1000);
    }, 1000);
}, 1000);
        

내가 처음 이걸 봤을 때 들여쓰기가 너무 깊어져서 읽기 힘들었다. 이를 해결하기 위해 나중에 프로미스와 async/await가 나왔다.

프로미스로 비동기 개선

프로미스(Promise)는 ES6에서 도입된 비동기 처리 방식으로, 콜백 헬을 피하고 코드를 더 깔끔하게 만든다. 프로미스는 작업의 상태를 나타내는 객체다.

프로미스 상태

- Pending: 작업 진행 중.

- Fulfilled: 작업 성공.

- Rejected: 작업 실패.

기본 사용

let promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve("성공!");
    }, 2000);
});
promise.then(function(result) {
    console.log(result); // 2초 후 "성공!"
});
        

resolve는 성공 시 호출, reject는 실패 시 호출한다. 내가 처음 프로미스를 썼을 때 체이닝이 가능하다는 점이 인상 깊었다:

promise
    .then(function(result) {
        console.log(result);
        return "다음 단계";
    })
    .then(function(next) {
        console.log(next);
    })
    .catch(function(error) {
        console.log("에러: ", error);
    });
// 출력: "성공!" -> "다음 단계"
        

catch로 에러를 잡을 수 있어서 안정적이다.

async/await로 더 간결하게

async/await는 ES2017에서 추가된 비동기 방식으로, 프로미스를 더 직관적으로 쓸 수 있게 한다.

기본 사용

async function fetchData() {
    let promise = new Promise((resolve) => {
        setTimeout(() => resolve("데이터 도착!"), 2000);
    });
    let result = await promise;
    console.log(result);
}
fetchData(); // 2초 후 "데이터 도착!"
        

async 함수 안에서 await를 쓰면 프로미스가 완료될 때까지 기다린다. 내가 처음 이걸 썼을 때 동기 코드처럼 보이는데 비동기라 놀랐다.

에러 처리

async function fetchData() {
    try {
        let promise = new Promise((resolve, reject) => {
            setTimeout(() => reject("에러 발생!"), 2000);
        });
        let result = await promise;
        console.log(result);
    } catch (error) {
        console.log("에러: ", error);
    }
}
fetchData(); // 2초 후 "에러: 에러 발생!"
        

try-catch로 깔끔하게 에러를 잡는다.

Async Await Example

실습 예제

비동기를 활용한 실습을 해보자:

<button id="fetchButton">데이터 가져오기</button>
<div id="result"></div>
<script>
    function fetchData() {
        return new Promise((resolve) => {
            setTimeout(() => resolve("서버에서 온 데이터"), 2000);
        });
    }

    async function loadData() {
        let button = document.getElementById("fetchButton");
        let resultDiv = document.getElementById("result");
        button.addEventListener("click", async () => {
            resultDiv.textContent = "로딩 중...";
            try {
                let data = await fetchData();
                resultDiv.textContent = data;
            } catch (error) {
                resultDiv.textContent = "에러 발생!";
            }
        });
    }
    loadData();
</script>

버튼 클릭 시 2초 후 데이터를 표시한다. 이벤트와 비동기를 결합한 예제다.

 

주의할 점

콜백 중첩은 피하고, 프로미스나 async/await를 쓰자. 비동기 작업은 순서를 잘 관리해야 한다. 에러 처리를 반드시 추가하자.

결론

이번 포스팅에서 자바스크립트 비동기의 기초를 다뤘다. 콜백, 프로미스, async/await를 배웠으니 이제 시간이 걸리는 작업도 효율적으로 처리할 수 있다. 다음 글에선 비동기의 이벤트 루프와 고급 활용법을 알아보자.

자바스크립트 이벤트 기초 - JavaScript 시리즈 #7

자바스크립트 이벤트 기초

지금까지 변수와 데이터 타입, 조건문과 반복문, 함수, 배열, 객체를 배웠다면, 이번엔 웹 페이지에서 사용자와 상호작용할 수 있게 해주는 이벤트를 배워볼 거다. 이벤트는 자바스크립트에서 버튼 클릭, 마우스 이동, 키보드 입력 같은 사용자 행동에 반응하는 핵심적인 방법으로, 동적인 웹을 만드는 데 필수적이다. 이벤트를 잘 익히면 정적인 HTML을 살아있는 웹 페이지로 바꿀 수 있다.

내가 처음 코딩을 배울 때 버튼을 눌렀을 때 반응이 없어서 답답했는데, 이벤트를 배우고 나니까 웹이 정말 살아있다는 느낌을 받았다. 이번 글에선 이벤트의 정의와 종류, 이벤트 핸들러 등록 방법, 이벤트 객체 활용법, 그리고 실습 예제를 상세히 다룰 거다. 천천히 따라오면서 콘솔이나 HTML 파일에서 직접 실행해보면 이벤트의 매력을 느낄 수 있을 거다. 이벤트는 단순히 반응을 추가하는 데 그치지 않고, 사용자 경험을 개선하고 웹 애플리케이션의 상호작용성을 높이는 데 큰 역할을 한다.

앞서 배운 개념들과 이벤트를 결합하면 더 실용적인 코드를 작성할 수 있다. 예를 들어, 배열에 저장된 데이터를 버튼 클릭으로 표시하거나, 객체의 속성을 키보드 입력으로 수정하는 작업을 쉽게 구현할 수 있다.

이벤트란 무엇인가?

이벤트(Event)는 웹 페이지에서 사용자가 특정 행동을 했을 때 발생하는 신호다. 자바스크립트는 이 이벤트를 감지해서 미리 정의한 코드를 실행할 수 있게 해준다. 예를 들어, 사용자가 버튼을 클릭하거나, 마우스를 움직이거나, 키보드를 누르는 행위가 모두 이벤트로 인식된다. 이벤트를 쓰면 정적인 HTML에 동적인 기능을 추가해서 사용자와 상호작용할 수 있는 웹을 만들 수 있다.

예를 들어, 버튼을 눌렀을 때 메시지를 띄우고 싶다면 이벤트가 필요하다:

<button id="myButton">클릭하세요</button>
<script>
    document.getElementById("myButton").onclick = function() {
        alert("버튼이 클릭됐어요!");
    };
</script>

이 코드를 실행하면 버튼을 누를 때마다 경고창이 뜬다. 내가 처음 이벤트를 배울 때 이런 간단한 반응이 신기했는데, 나중에 보니 이벤트는 웹 개발의 핵심이었다. 자바스크립트에서 이벤트는 DOM(Document Object Model)과 밀접하게 연결돼 있어서, HTML 요소와 사용자 행동을 연결하는 다리 역할을 한다.

이벤트는 사용자 행동뿐만 아니라 시스템에서 발생하는 일(예: 페이지 로드 완료, 네트워크 응답 등)도 포함한다. 실무에선 이벤트로 버튼 클릭을 처리하거나, 폼 제출을 확인하거나, 스크롤에 따라 애니메이션을 넣는 경우가 많다. 이벤트는 자바스크립트가 웹을 동적으로 만드는 핵심 도구로, 이를 통해 사용자가 직접 경험할 수 있는 인터랙티브한 환경을 제공한다.

이벤트의 종류

자바스크립트에서 다룰 수 있는 이벤트는 정말 다양하다. 몇 가지 주요 이벤트를 소개하면:

1. 마우스 이벤트

- click: 요소를 클릭했을 때.

- mouseover: 마우스가 요소 위로 올라갈 때.

- mouseout: 마우스가 요소를 떠날 때.

- mousedown: 마우스 버튼을 누를 때.

- mouseup: 마우스 버튼을 뗄 때.

예를 들어, 버튼에 마우스를 올리면 색이 바뀌게 할 수 있다:

<button id="hoverButton">마우스를 올려보세요</button>
<script>
    let btn = document.getElementById("hoverButton");
    btn.onmouseover = function() {
        btn.style.backgroundColor = "lightblue";
    };
    btn.onmouseout = function() {
        btn.style.backgroundColor = "";
    };
</script>

내가 처음 이걸 썼을 때 마우스 반응이 이렇게 쉬울 수가 있나 싶었다.

2. 키보드 이벤트

- keydown: 키를 누를 때.

- keyup: 키를 뗄 때.

- keypress: 키를 눌렀을 때(현재는 잘 안 쓰임).

예를 들어, 엔터 키를 눌렀을 때 메시지를 띄울 수 있다:

<input id="myInput" type="text">
<script>
    document.getElementById("myInput").onkeydown = function(event) {
        if (event.key === "Enter") {
            console.log("엔터를 눌렀어요!");
        }
    };
</script>

3. 폼 이벤트

- submit: 폼을 제출할 때.

- change: 입력값이 바뀔 때.

- focus: 요소에 포커스가 갈 때.

- blur: 요소에서 포커스가 떠날 때.

예를 들어, 입력값이 바뀔 때 확인할 수 있다:

<input id="myInput" type="text">
<script>
    document.getElementById("myInput").onchange = function() {
        console.log("입력값이 바뀌었어요!");
    };
</script>

4. 창/문서 이벤트

- load: 페이지가 로드 완료될 때.

- resize: 창 크기가 바뀔 때.

- scroll: 스크롤할 때.

예를 들어, 페이지 로드 완료를 감지한다:

<script>
    window.onload = function() {
        console.log("페이지가 로드됐어요!");
    };
</script>

내가 처음 이벤트 종류를 접했을 때 이렇게 많을 줄 몰라서 놀랐다. 이벤트는 웹에서 거의 모든 상호작용을 다룰 수 있게 해준다.

Event Types

이벤트 핸들러 등록 방법

이벤트를 감지하고 반응하려면 이벤트 핸들러를 등록해야 한다. 자바스크립트에서 핸들러를 등록하는 방법은 세 가지가 주로 쓰인다.

1. 인라인 이벤트 핸들러

HTML 태그에 직접 속성으로 추가한다:

<button onclick="alert('클릭!')">클릭하세요</button>

간단하지만 코드가 섞여서 가독성이 떨어진다. 내가 처음엔 이걸 썼는데, 나중엔 관리하기 힘들어서 다른 방법을 선호하게 됐다.

2. DOM 속성으로 등록

자바스크립트에서 요소의 이벤트 속성에 함수를 지정한다:

<button id="myButton">클릭하세요</button>
<script>
    document.getElementById("myButton").onclick = function() {
        console.log("버튼 클릭!");
    };
</script>

이 방식은 HTML과 JS를 분리해서 깔끔하다. 단, 한 이벤트에 하나만 등록할 수 있다.

3. addEventListener 사용

가장 현대적이고 유연한 방법이다:

<button id="myButton">클릭하세요</button>
<script>
    let btn = document.getElementById("myButton");
    btn.addEventListener("click", function() {
        console.log("버튼 클릭!");
    });
</script>

같은 이벤트에 여러 핸들러를 추가할 수 있다:

btn.addEventListener("click", function() {
    console.log("또 다른 반응!");
});

내가 처음 addEventListener를 썼을 때 유연성에 감동받았다. 실무에서 가장 추천하는 방식이다.

Event Handler

이벤트 객체 활용

이벤트가 발생하면 이벤트 객체(Event Object)가 자동으로 전달된다. 이 객체는 이벤트에 대한 정보를 담고 있다.

기본 사용

<button id="myButton">클릭하세요</button>
<script>
    document.getElementById("myButton").addEventListener("click", function(event) {
        console.log(event);
    });
</script>

이벤트 객체는 클릭 위치, 키 값, 타겟 요소 같은 데이터를 제공한다:

document.getElementById("myButton").addEventListener("click", function(event) {
    console.log("클릭 위치: ", event.clientX, event.clientY);
});
// 클릭 시 마우스 좌표 출력

키보드 이벤트 활용

<input id="myInput" type="text">
<script>
    document.getElementById("myInput").addEventListener("keydown", function(event) {
        console.log("눌린 키: ", event.key);
    });
</script>

내가 처음 이벤트 객체를 썼을 때 이렇게 상세한 정보가 나오는 줄 몰라서 놀랐다.

실습 예제

이벤트를 활용한 실습을 해보자:

<button id="colorButton">색상 변경</button>
<input id="textInput" type="text" placeholder="텍스트 입력">
<script>
    let btn = document.getElementById("colorButton");
    let input = document.getElementById("textInput");
    let colors = ["red", "blue", "green"];
    let index = 0;

    btn.addEventListener("click", function() {
        document.body.style.backgroundColor = colors[index];
        index = (index + 1) % colors.length;
    });

    input.addEventListener("keyup", function(event) {
        if (event.key === "Enter") {
            console.log("입력값: ", input.value);
            input.value = "";
        }
    });
</script>

버튼 클릭으로 배경색을 바꾸고, 입력창에서 엔터를 누르면 값을 출력한다. 이벤트와 배열을 결합한 예제다.

Event Example

주의할 점

이벤트 핸들러를 중복 등록하면 예상치 못한 동작이 생길 수 있다. 이벤트 객체의 속성을 사용할 때 브라우저 호환성을 확인하자. 기본 동작을 막고 싶을 땐 event.preventDefault()를 써야 한다.

결론

이번 포스팅에서 자바스크립트 이벤트의 기초를 다뤘다. 이벤트 종류, 핸들러 등록, 객체 활용까지 배웠으니 이제 웹에서 사용자와 상호작용할 수 있다. 다음 글에선 이벤트 버블링과 캡처링 같은 고급 개념을 알아보자.

자바스크립트 객체 기초 - JavaScript 시리즈 #6

자바스크립트 객체 기초

지금까지 변수와 데이터 타입, 조건문과 반복문, 함수, 배열을 배웠다면, 이번엔 데이터를 구조적으로 다룰 수 있는 객체를 배워볼 거다. 객체는 자바스크립트에서 데이터를 키-값 쌍으로 저장하고 관리하는 강력한 방법으로, 실무에서 엄청나게 자주 쓰인다. 객체를 잘 익히면 단순한 값 나열을 넘어 복잡한 데이터 구조를 다룰 수 있는 능력이 생긴다.

내가 처음 코딩을 배울 때 객체를 몰라서 변수와 배열로만 데이터를 관리하려고 했는데, 객체를 배우고 나니까 훨씬 체계적으로 코드를 짤 수 있게 됐다. 이번 글에선 객체의 정의와 생성 방법, 속성과 메서드, 객체와 배열의 결합, 그리고 기본적인 활용법을 상세히 다룰 거다. 천천히 따라오면서 콘솔이나 HTML 파일에서 직접 실행해보면 객체의 유연함과 강력함을 실감할 수 있을 거다. 객체는 단순히 데이터를 저장하는 데 그치지 않고, 데이터를 구조화하고 동작까지 추가할 수 있게 해준다.

앞서 배운 개념들과 객체를 결합하면 더 실용적인 코드를 작성할 수 있다. 예를 들어, 학생의 이름과 점수를 배열로만 관리하던 걸 객체로 바꾸면 이름과 점수를 한 번에 묶어서 처리할 수 있고, 함수로 점수를 계산하거나 조건문으로 특정 조건을 확인하는 작업도 훨씬 간편해진다. 이번 포스팅에서 객체의 기초를 다루며, 자바스크립트의 기본기를 한층 더 강화해보자.

객체란 무엇인가?

객체(Object)는 자바스크립트에서 데이터를 키-값 쌍(Key-Value Pair)으로 저장하는 데이터 구조다. 배열이 값을 순서대로 나열한다면, 객체는 각 값을 이름(키)으로 식별해서 저장한다. 이 키는 문자열이나 심볼로 정의되며, 값은 숫자, 문자열, 배열, 심지어 다른 객체까지 될 수 있다. 객체를 쓰면 데이터를 논리적으로 묶어서 관리할 수 있어서 코드의 가독성과 유지보수성이 크게 좋아진다.

예를 들어, 한 명의 학생 데이터를 배열로 저장하려면 이렇게 해야 한다:

let student = ["홍길동", 20, 85];

이렇게 하면 인덱스로 접근해야 해서 student[0]이 이름인지, student[1]이 나이인지 기억해야 한다. 하지만 객체를 쓰면:

let student = {
    name: "홍길동",
    age: 20,
    score: 85
};

이렇게 키로 바로 의미를 알 수 있다. 내가 처음 객체를 배울 때 이 직관성에 감탄했는데, 데이터를 이름으로 접근하니까 코드가 훨씬 명확해졌다. 자바스크립트에서 객체는 배열과 달리 순서가 없고, 키를 통해 값을 찾는 방식이라 더 유연하다.

객체는 자바스크립트의 핵심 데이터 타입 중 하나로, 함수나 배열도 사실 객체의 일종이다. 객체를 쓰면 단순히 데이터를 저장하는 데서 끝나지 않고, 데이터를 구조화하고 동작(메서드)을 추가할 수 있다. 실무에선 사용자 정보, 설정값, API 응답 같은 복잡한 데이터를 객체로 다루는 경우가 많다.

객체는 단순히 값을 저장하는 도구를 넘어, 데이터를 논리적으로 묶고 그 데이터와 관련된 동작까지 정의할 수 있게 해준다. 예를 들어, 학생 객체에 점수를 계산하는 기능을 추가하거나, 사용자의 상태를 관리하는 코드를 객체 안에 넣을 수 있다. 이런 점 때문에 객체는 자바스크립트에서 정말 중요한 개념으로 여겨진다.

객체 생성 방법

자바스크립트에서 객체를 만드는 방법은 주로 두 가지가 있다: 객체 리터럴과 Object 생성자다. 각각의 특징과 사용법을 살펴보자.

1. 객체 리터럴(Object Literal)

가장 간단하고 널리 쓰이는 방법으로, 중괄호 {}를 사용한다:

let person = {
    name: "홍길동",
    age: 25,
    city: "서울"
};
console.log(person); // { name: "홍길동", age: 25, city: "서울" }

키와 값은 :로 연결하고, 각 쌍은 쉼표로 구분한다. 빈 객체도 만들 수 있다:

let emptyObject = {};
console.log(emptyObject); // {}

내가 처음 객체를 쓸 때 이 방식이 직관적이라 바로 익숙해졌다. 키는 따옴표 없이 써도 되지만, 특수문자나 공백이 들어가면 따옴표를 써야 한다:

let user = {
    "first name": "길동",
    "email-address": "gildong@example.com"
};

객체 리터럴은 간단하고 읽기 쉬워서 대부분의 상황에서 추천한다.

2. Object 생성자

Object 객체를 사용해서도 객체를 만들 수 있다:

let person = new Object();
person.name = "이순신";
person.age = 30;
console.log(person); // { name: "이순신", age: 30 }

이 방식은 빈 객체를 만든 뒤 속성을 하나씩 추가한다. 내가 처음 이걸 썼을 때 리터럴보다 번거롭다고 느꼈는데, 동적으로 속성을 추가할 때 유용하다.

객체 속성과 접근 방법

객체를 생성했으면 속성(Property)에 접근하고 수정하는 법을 알아야 한다.

속성 접근

객체 속성에 접근하는 방법은 두 가지다: 점 표기법(Dot Notation)과 대괄호 표기법(Bracket Notation).

- 점 표기법:

let person = {
    name: "김유신",
    age: 28
};
console.log(person.name); // "김유신"
console.log(person.age); // 28

- 대괄호 표기법:

console.log(person["name"]); // "김유신"
console.log(person["age"]); // 28

대괄호 표기법은 변수나 특수문자가 있는 키에 유용하다:

let key = "name";
console.log(person[key]); // "김유신"
let user = {
    "first-name": "유신"
};
console.log(user["first-name"]); // "유신"

내가 처음엔 점 표기법만 썼는데, 동적 키를 다룰 때 대괄호가 필수라는 걸 알게 됐다.

속성 추가와 수정

객체는 동적이어서 속성을 언제든 추가하거나 바꿀 수 있다:

let person = {
    name: "홍길동"
};
person.age = 25; // 추가
console.log(person); // { name: "홍길동", age: 25 }
person.name = "이순신"; // 수정
console.log(person); // { name: "이순신", age: 25 }

속성 삭제

delete 연산자로 속성을 제거한다:

delete person.age;
console.log(person); // { name: "이순신" }

내가 처음 객체 수정할 때 이렇게 유연한 점이 신기했는데, 실무에서 데이터를 동적으로 다룰 때 정말 유용하다.

객체와 메서드

객체는 속성 외에도 동작(메서드)을 정의할 수 있다. 메서드는 객체 안에 함수를 추가한 거다.

메서드 정의

let person = {
    name: "김유신",
    sayHello: function() {
        console.log("안녕, 나는 " + this.name + "이야!");
    }
};
person.sayHello(); // "안녕, 나는 김유신이야!"

this는 객체 자신을 가리킨다. ES6부터는 더 간결하게 쓸 수 있다:

let person = {
    name: "홍길동",
    sayHello() {
        console.log("안녕, 나는 " + this.name + "이야!");
    }
};
person.sayHello(); // "안녕, 나는 홍길동이야!"

내가 처음 메서드를 썼을 때 this가 헷갈렸는데, 객체 안에서 자기 자신을 참조한다고 생각하면 된다.

객체와 배열 결합

객체와 배열을 함께 쓰면 더 강력하다. 예를 들어, 학생 목록을 배열로 만들고 각 학생을 객체로 정의할 수 있다:

let students = [
    { name: "홍길동", score: 85 },
    { name: "이순신", score: 92 },
    { name: "김유신", score: 78 }
];
console.log(students[1].name); // "이순신"

내가 처음 이걸 썼을 때 데이터 구조가 훨씬 명확해져서 좋았다.

실습 예제

객체를 활용한 실습을 해보자:

let student = {
    name: "홍길동",
    scores: [85, 90, 88],
    getAverage: function() {
        let sum = 0;
        for (let score of this.scores) {
            sum += score;
        }
        return sum / this.scores.length;
    }
};
console.log(student.getAverage()); // 87.666...

let users = [
    { name: "이순신", age: 30 },
    { name: "김유신", age: 25 }
];
for (let user of users) {
    console.log(user.name + "은 " + user.age + "살이에요.");
}
// 출력: "이순신은 30살이에요.", "김유신은 25살이에요."

평균 계산과 사용자 목록 출력 예제를 만들어봤다. 객체와 배열을 결합하니 실용적인 코드가 된다.

주의할 점

객체는 참조 타입이라 복사 시 주의해야 한다. this의 값은 호출 방식에 따라 달라질 수 있으니 잘 확인하자. 속성 이름은 중복되지 않게 관리해야 한다.

결론

이번 포스팅에서 자바스크립트 객체의 기초를 다뤘다. 객체 생성, 속성과 메서드, 배열과의 결합까지 배웠으니 이제 데이터를 구조적으로 다룰 수 있다. 다음 글에선 객체의 프로토타입과 상속을 더 깊이 파고들어 보자.

자바스크립트 배열 기초 - JavaScript 시리즈 #5

자바스크립트 배열 기초

지금까지 변수와 데이터 타입, 조건문과 반복문, 함수를 배웠다면, 이번엔 데이터를 리스트 형태로 다루는 배열을 배워볼 거다. 배열은 자바스크립트에서 여러 값을 한 번에 관리할 수 있는 강력한 도구로, 프로그래밍에서 자주 쓰이는 데이터 구조다. 배열을 잘 익히면 데이터를 효율적으로 저장하고 처리하는 방법을 터득할 수 있다.

내가 처음 코딩을 배울 때 배열을 몰라서 변수 여러 개를 만들어서 관리했는데, 배열을 알게 되니까 코드가 훨씬 간단해졌다. 이번 글에선 배열의 정의와 생성 방법, 기본적인 사용법, 배열 메서드, 반복문과의 결합을 다룰 거다. 천천히 따라오면서 콘솔이나 HTML 파일에서 직접 실행해보면 배열의 유용함을 실감할 수 있을 거다. 배열은 단순히 값을 나열하는 데 그치지 않고, 데이터를 조작하고 변환하는 데 핵심적인 역할을 한다.

앞서 배운 함수와 조건문, 반복문을 배열과 함께 쓰면 더 강력한 코드를 짤 수 있다. 예를 들어, 학생 점수 목록을 배열로 저장하고, 함수로 평균을 계산하거나 조건에 따라 특정 점수를 골라내는 작업을 쉽게 할 수 있다. 이번 포스팅에서 배열의 기초부터 실습까지 상세히 다루며, 자바스크립트 기본기를 한 단계 더 끌어올려보자.

배열이란 무엇인가?

배열(Array)은 여러 개의 값을 순서대로 저장하는 데이터 구조다. 자바스크립트에서 배열은 변수 하나에 여러 값을 담을 수 있게 해줘서, 데이터를 리스트처럼 관리할 때 쓰인다. 배열의 각 값은 인덱스(Index)라는 숫자로 접근할 수 있는데, 인덱스는 0부터 시작한다. 배열을 쓰면 반복 작업이나 데이터 집합을 다룰 때 매우 편리하다.

예를 들어, 학생 세 명의 이름을 각각 변수로 저장하려면 let name1 = "홍길동";, let name2 = "이순신";, let name3 = "김유신"; 이렇게 해야 한다. 하지만 배열을 쓰면:

let names = ["홍길동", "이순신", "김유신"];

이렇게 한 줄로 끝난다. 내가 처음 배열을 배울 때 이 간단함에 감탄했는데, 나중에 보니 배열은 단순히 값을 모으는 데서 끝나지 않고 다양한 연산과 조작을 가능하게 해줬다. 자바스크립트에서 배열은 객체의 일종이지만, 순서가 있는 리스트처럼 동작해서 다른 언어의 배열과 비슷하게 느껴진다.

배열은 데이터를 효율적으로 관리하고, 반복문이나 함수와 결합해서 복잡한 작업을 쉽게 처리할 수 있게 해준다. 실무에선 사용자 목록, 제품 정보, 점수 데이터 같은 걸 배열로 다루는 경우가 많다.

배열 생성 방법

자바스크립트에서 배열을 만드는 방법은 두 가지가 주로 쓰인다: 배열 리터럴과 Array 생성자다. 각각의 특징과 사용법을 보자.

1. 배열 리터럴(Array Literal)

가장 간단하고 많이 쓰이는 방법으로, 대괄호 []를 사용한다:

let fruits = ["사과", "바나나", "오렌지"];
console.log(fruits); // ["사과", "바나나", "오렌지"]

배열 안에 값을 쉼표로 구분해서 넣으면 된다. 빈 배열도 만들 수 있다:

let emptyArray = [];
console.log(emptyArray); // []

내가 처음 배열을 쓸 때 이 방식이 직관적이라 금방 익숙해졌다. 배열 리터럴은 간단하고 읽기 쉬워서 추천한다.

2. Array 생성자

Array 객체를 사용해서 배열을 만들 수도 있다:

let numbers = new Array(1, 2, 3);
console.log(numbers); // [1, 2, 3]

또는 길이만 지정해서 빈 배열을 만들 수 있다:

let empty = new Array(3);
console.log(empty); // [undefined, undefined, undefined]

이건 길이 3인 배열을 만들지만 값은 채워지지 않는다. 내가 처음 이걸 썼을 때 undefined가 나와서 당황했는데, 주로 동적으로 채울 때 유용하다.

배열 기본 사용법

배열을 생성했으면 값을 읽고 쓰는 법을 알아야 한다.

인덱스로 접근

배열의 각 요소는 인덱스로 접근한다:

let fruits = ["사과", "바나나", "오렌지"];
console.log(fruits[0]); // "사과"
console.log(fruits[1]); // "바나나"

인덱스는 0부터 시작하니까 첫 번째 요소는 [0], 두 번째는 [1]이다. 값을 바꿀 수도 있다:

fruits[1] = "포도";
console.log(fruits); // ["사과", "포도", "오렌지"]

내가 처음 인덱스 쓸 때 1부터 시작인 줄 알고 헷갈렸는데, 0부터라는 걸 기억하면 된다.

길이 확인

length 속성으로 배열의 길이를 알 수 있다:

let fruits = ["사과", "바나나", "오렌지"];
console.log(fruits.length); // 3

길이를 이용해서 마지막 요소도 쉽게 접근할 수 있다:

console.log(fruits[fruits.length - 1]); // "오렌지"

배열 길이는 동적이라 요소를 추가하거나 빼면 자동으로 바뀐다.

배열 메서드

자바스크립트 배열은 다양한 메서드를 제공해서 데이터를 쉽게 조작할 수 있다. 몇 가지 기본 메서드를 보자.

1. 요소 추가/제거

- push: 배열 끝에 요소 추가

let fruits = ["사과"];
fruits.push("바나나");
console.log(fruits); // ["사과", "바나나"]

- pop: 배열 끝 요소 제거

fruits.pop();
console.log(fruits); // ["사과"]

- unshift: 배열 앞에 요소 추가

fruits.unshift("오렌지");
console.log(fruits); // ["오렌지", "사과"]

- shift: 배열 앞 요소 제거

fruits.shift();
console.log(fruits); // ["사과"]

내가 처음 pushpop을 썼을 때 배열이 스택처럼 동작하는 느낌이 재밌었다.

2. 배열 탐색과 변환

- indexOf: 요소의 인덱스 찾기

let fruits = ["사과", "바나나", "오렌지"];
console.log(fruits.indexOf("바나나")); // 1
console.log(fruits.indexOf("포도")); // -1 (없으면 -1)

- includes: 요소 포함 여부 확인

console.log(fruits.includes("오렌지")); // true
console.log(fruits.includes("포도")); // false

- join: 배열을 문자열로 변환

console.log(fruits.join(", ")); // "사과, 바나나, 오렌지"

내가 indexOf를 처음 썼을 때 -1이 나와서 당황했는데, 없는 걸 찾으면 -1을 반환한다는 걸 알면 유용하다.

배열과 반복문

배열은 반복문과 함께 쓰면 진가를 발휘한다.

for 문으로 순회

let fruits = ["사과", "바나나", "오렌지"];
for (let i = 0; i < fruits.length; i++) {
    console.log(fruits[i]);
}
// 출력: "사과", "바나나", "오렌지"

for...of 문

ES6에서 추가된 더 간단한 방법:

for (let fruit of fruits) {
    console.log(fruit);
}
// 출력: "사과", "바나나", "오렌지"

내가 처음 for...of를 썼을 때 인덱스 없이 바로 값을 가져오는 게 편리해서 자주 쓰게 됐다.

실습 예제

배열을 활용한 실습을 해보자:

function getAverage(scores) {
    let sum = 0;
    for (let score of scores) {
        sum += score;
    }
    return sum / scores.length;
}
let scores = [85, 90, 95, 88];
console.log(getAverage(scores)); // 89.5

let names = ["홍길동", "이순신", "김유신"];
for (let i = 0; i < names.length; i++) {
    if (names[i].includes("신")) {
        console.log(names[i] + "은 '신'이 포함돼요!");
    }
}
// 출력: "이순신은 '신'이 포함돼요!", "김유신은 '신'이 포함돼요!"

평균 계산과 이름 필터링을 해봤다. 배열과 함수, 반복문을 결합하니 실용적인 코드가 된다.

주의할 점

인덱스는 0부터 시작하니 헷갈리지 않게 주의하자. length는 동적이니까 배열 수정 시 확인해야 한다. 메서드마다 배열을 변경하는지 여부를 잘 알아야 한다.

결론

이번 포스팅에서 자바스크립트 배열의 기초를 다뤘다. 배열 생성, 기본 사용법, 메서드, 반복문 활용까지 배웠으니 이제 데이터를 리스트로 다룰 수 있다. 다음 글에선 배열 메서드를 더 깊이 파고들어 고급 활용법을 알아보자.

+ Recent posts