Node.js 최신 자바스크립트 트렌드와 프론트엔드 기술 탐구
호출 스택과 이벤트 루프
- 호출 스택(Call Stack): 함수 호출이 저장되는 LIFO(Last In, First Out) 구조의 스택. 함수 실행이 완료되면 스택에서 제거됨
- 이벤트 루프(Event Loop): 비동기 처리 시 이벤트나 콜백을 관리하고, 실행 순서를 결정. 태스크 큐(Task Queue)에서 대기하고 있는 콜백을 호출 스택이 비어 있을 때 실행
ES2015+ 문법
- 변수 선언: let, const로 변수 선언 가능. 블록 스코프 지원
- 템플릿 문자열: 백틱(`) 사용하여 문자열 내 변수 삽입 가능
- 화살표 함수: 간결한 함수 표현. this가 외부 함수에서 상속
- 구조 분해 할당: 객체나 배열의 속성을 변수로 쉽게 할당
- 클래스: 프로토타입 기반 코드를 클래스 문법으로 간결하게 표현 가능
- 프로미스와 async/await: 비동기 코드의 가독성을 높이고, 콜백 지옥 해결
프런트엔드 자바스크립트
- AJAX: 서버와 비동기적으로 통신하여 데이터를 주고받을 수 있는 기술. XMLHttpRequest 또는 axios 사용
- FormData: HTML form 데이터를 AJAX 요청으로 송신하기 위해 사용
- encodeURIComponent / decodeURIComponent: URI의 특정 문자를 인코딩/디코딩
- 데이터 속성(Data Attribute): HTML 태그에 데이터를 저장하고 dataset을 통해 접근
const와 let
const와 let은 ES6에서 도입된 변수 선언 키워드로, var의 스코프 문제를 해결하기 위해 만들어졌습니다.
- const: 상수를 선언할 때 사용합니다. 한번 값이 할당되면 변경할 수 없습니다.
- let: 블록 스코프 지역 변수를 선언할 때 사용하며, 선언된 블록, 문 또는 표현식에서만 유효합니다.
const PI = 3.14; // 값을 변경할 수 없음
let score = 10; // 블록 스코프 내에서 값 변경 가능
클래스 (Class)
클래스는 객체 지향 프로그래밍을 위한 템플릿입니다. 메서드, 생성자, 상속 등을 제공합니다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const dog = new Dog('Rex');
dog.speak(); // "Rex barks."
프로미스 (Promise)
프로미스는 비동기 작업의 성공 또는 실패의 결과를 나중에 처리할 수 있게 해 줍니다. 기본적으로, 프로미스는 세 가지 상
태를 가집니다: 대기(pending), 이행(fulfilled), 거부(rejected). 프로미스의 사용 방법을 이해하기 쉽도록 기본적인 예제를 통해 설명하겠습니다.
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => resolve("data received"), 3000);
});
fetchData.then(data => console.log(data));
프로미스 기본 구조
프로미스는 new Promise 생성자를 통해 생성되며, 이 생성자는 resolve와 reject라는 두 가지 매개변수를 갖는 함수를 인자로 받습니다.
// 예제: 파일 다운로드를 시뮬레이션하는 프로미스
const downloadFile = new Promise((resolve, reject) => {
// 비동기 작업을 시뮬레이션
const fileDownloaded = true; // 파일 다운로드 성공 여부
setTimeout(() => {
if (fileDownloaded) {
resolve("파일 다운로드 완료"); // 성공 시 resolve 호출
} else {
reject("다운로드 실패"); // 실패 시 reject 호출
}
}, 3000); // 3초 후 결과 반환
});
downloadFile
.then(result => console.log(result)) // 성공 처리
.catch(error => console.log(error)) // 실패 처리
.finally(() => console.log("비동기 작업 완료")); // 최종적으로 항상 실행
프로미스 체이닝
프로미스는 여러 비동기 작업을 순서대로 처리할 때 유용하게 사용됩니다. 이를 '프로미스 체이닝'이라고 합니다. 다음 then 호출의 결과는 이전 then 호출의 반환값에 의존합니다.
// 예제: 파일 다운로드 후 파일 분석
downloadFile
.then(result => {
console.log(result); // "파일 다운로드 완료"
return "파일 분석 시작"; // 다음 then으로 전달
})
.then(status => {
console.log(status); // "파일 분석 시작"
return "파일 분석 완료"; // 다음 then으로 전달
})
.then(finalResult => console.log(finalResult)) // "파일 분석 완료"
.catch(error => console.log(error)) // 에러 처리
.finally(() => console.log("모든 작업 완료")); // 최종적으로 항상 실행
async / await
async와 await는 JavaScript의 비동기 처리 패턴 중 하나로, 기존의 복잡한 프로미스 체이닝(Promise chaining)을 보다 간결하고 동기적인(synchronous) 방식으로 작성할 수 있게 도와주는 문법입니다. 이 문법을 사용하면 비동기 코드를 마치 동기 코드처럼 읽고 작성할 수 있습니다.
async/await 기본 개념
- async 함수: 함수 앞에 async 키워드를 추가하면 해당 함수는 항상 프로미스를 반환합니다. 함수 내부에서 return 문을 사용하면, 이 값은 프로미스에 의해 자동으로 resolve 됩니다. 만약 함수 실행 중 예외가 발생하면, 그 예외는 프로미스의 reject가 됩니다.
- await 연산자: await 키워드는 async 함수 내부에서만 사용할 수 있으며, 프로미스가 완료될 때까지 함수의 실행을 일시 중지하고, 프로미스의 결과 값을 반환합니다. 프로미스가 거부(rejected)될 경우, await는 예외를 발생시킵니다.
// 프로미스를 반환하는 함수들
function downloadFile(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("파일 다운로드 완료");
resolve("다운로드한 파일 데이터"); // 다운로드된 파일의 내용
}, 2000);
});
}
function analyzeFile(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("파일 분석 완료");
resolve("분석 결과"); // 분석된 결과
}, 2000);
});
}
// async/await를 사용한 함수
async function processFile(url) {
try {
const fileData = await downloadFile(url); // 파일 다운로드
const analysisResult = await analyzeFile(fileData); // 파일 분석
console.log(analysisResult);
} catch (error) {
console.error("에러 발생:", error);
}
}
processFile("http://example.com/file");
이 예제에서 processFile 함수는 async 함수로 선언되었으며, 이 함수 내부에서 downloadFile과 analyzeFile 함수는 각각의 비동기 작업이 완료될 때까지 기다리는데 await를 사용합니다. 두 함수 모두 프로미스를 반환하므로, await를 사용할 수 있습니다. 만약 어느 하나의 작업에서 예외가 발생하면, catch 블록이 실행되어 에러를 처리합니다.
async와 await를 사용하면 프로미스를 사용하는 코드보다 훨씬 가독성이 좋고, 관리하기 쉬운 코드를 작성할 수 있습니다. 이러한 특성 덕분에 복잡한 비동기 로직을 구현할 때 코드의 이해도를 높일 수 있습니다.
Map / Set
Map은 키-값 쌍을 저장하는 구조이며, Set은 중복 없는 값을 저장합니다.
let map = new Map();
map.set('name', 'John');
console.log(map.get('name')); // "John"
let set = new Set();
set.add('apple');
set.add('banana');
set.add('apple');
console.log(set.has('apple')); // true
console.log(set.size); // 2