모던 JS 문법
-
모던 JS 문법은 ES6(ES2015) 이후 도입된 기능들로, 개발자 경험을 개선하고 코드를 간결하게 만듭니다.
- Arrow Function, Spread 구문, Optional Chaining 외에도 Destructuring, Template Literals, Default Parameters 자주 사용되는 문법을 추가로 다루어보겠습니다.
Arrow Function (화살표 함수)
- Arrow Function은 함수를 간결하게 정의하는 방법으로, function 키워드 대신 =>를 사용합니다. this 바인딩이 렉시컬(정적) 스코프를 따르는 점이 특징입니다.
기본사용
// 일반 함수
function add(a, b) {
return a + b;
}
// Arrow Function
const addArrow = (a, b) => a + b; // 한 줄일 경우 중괄호와 return 생략 가능
console.log(add(3, 5)); // 8
console.log(addArrow(3, 5)); // 8
매개변수가 하나일 경우
- 괄호 생략 가능
const square = x => x * x;
console.log(square(4)); // 16
객체 반환
-객체를 반환할 때는 소괄호로 감싸야 함
const getPerson = name => ({ name: name, age: 30 });
console.log(getPerson("홍길동")); // { name: "홍길동", age: 30 }
this 바인딩 차이
- Arrow Functiuon은 this를 정의된 위치(렉시컬 스코프)에서 가져옵니다.
const obj = {
name: "김철수",
sayHello: function() {
setTimeout(function() {
console.log(this.name); // 일반 함수: this는 setTimeout의 this (undefined)
}, 1000);
setTimeout(() => {
console.log(this.name); // Arrow Function: this는 obj (김철수)
}, 1000);
}
};
obj.sayHello();
// 출력:
// undefined
// 김철수
활용 예시
- 배열 메소드와 함께
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
Spread 구문(전개 연산자, ...)
- Spread 구문은 배열이나 객체의 요소를 확장하거나 복사할 때 사용됩니다.
배열에서 사용
// 배열 복사
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // 얕은 복사
console.log(arr2); // [1, 2, 3]
// 배열 병합
const arr3 = [...arr1, 4, 5];
console.log(arr3); // [1, 2, 3, 4, 5]
// 배열 요소 전달
const sum = (a, b, c) => a + b + c;
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
객체에서 사용
// 객체 복사
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1 }; // 얕은 복사
console.log(obj2); // { a: 1, b: 2 }
// 객체 병합
const obj3 = { ...obj1, c: 3 };
console.log(obj3); // { a: 1, b: 2, c: 3 }
// 기존 속성 덮어쓰기
const obj4 = { ...obj1, a: 10 };
console.log(obj4); // { a: 10, b: 2 }
활용 예시
- 함수 인자
const user = { name: "홍길동", age: 25 };
const updatedUser = { ...user, age: 26, city: "서울" };
console.log(updatedUser); // { name: "홍길동", age: 26, city: "서울" }
Optional Chaining(?.)
- Optional Chaining은 객체 속성에 안전하게 접근할 때 사용됩니다. 속성이 존재하지 않을 경우 undefined를 반환하며, 오류를 방지합니다.
기본 사용
const user = {
name: "김철수",
address: { city: "부산" }
};
console.log(user.address?.city); // "부산"
console.log(user.phone?.number); // undefined (오류 없이 접근)
중첩된 객체
const data = {
user: {
profile: { age: 30 }
}
};
console.log(data.user?.profile?.age); // 30
console.log(data.user?.settings?.theme); // undefined
함수 호출에서 사용
const obj = {
sayHello: () => "안녕!"
};
console.log(obj.sayHello?.()); // "안녕!"
console.log(obj.sayGoodbye?.()); // undefined
활용 예시
- API 데이터 처리
const response = {
user: { name: "홍길동" }
};
const userName = response.user?.name ?? "익명"; // Optional Chaining + Nullish Coalescing
console.log(userName); // "홍길동"
Destructuring(구조 분해 할당)
- 객체나 배열에서 값을 추출해 변수로 바로 할당합니다.
객체 구조 분해
const person = { name: "홍길동", age: 22, city: "대구" };
// 기존 방식
// const name = person.name;
// const age = person.age;
// 구조 분해
const { name, age } = person;
console.log(name, age); // "홍길동" 22
// 별칭 사용
const { name: userName, city: location } = person;
console.log(userName, location); // "홍길동" "대구"
배열 구조 분해
const fruits = ["사과", "바나나", "포도"];
// 기존 방식
// const first = fruits[0];
// const second = fruits[1];
// 구조 분해
const [first, second] = fruits;
console.log(first, second); // "사과" "바나나"
// 일부 요소 건너뛰기
const [, , third] = fruits;
console.log(third); // "포도"
활용 예시
- 함수 매개변수
const printUser = ({ name, age }) => {
console.log(`${name}은 ${age}살입니다.`);
};
const user = { name: "김철수", age: 28 };
printUser(user); // "김철수은 28살입니다."
Template Literals(템플릿 리터럴)
- 백틱(`)을 사용해 문자열을 더 간결하게 작성합니다. 변수나 표현식을 ${}로 삽입 가능합니다.
const name = "홍길동";
const age = 25;
// 기존 방식
const greeting = "안녕, " + name + "! 나이는 " + age + "살이야.";
// 템플릿 리터럴
const greetingModern = `안녕, ${name}! 나이는 ${age}살이야.`;
console.log(greetingModern); // "안녕, 홍길동! 나이는 25살이야."
// 표현식 사용
console.log(`2 + 3 = ${2 + 3}`); // "2 + 3 = 5"
활용 예시
- HTML 생성
const user = { name: "이영희", age: 22 };
const html = `
<div>
<h1>${user.name}</h1>
<p>나이: ${user.age}</p>
</div>
`;
console.log(html);
Default Parameters(기본 매개변수)
- 함수 매개변수에 기본값을 설정합니다.
function greet(name = "익명", greeting = "안녕") {
return `${greeting}, ${name}!`;
}
console.log(greet()); // "안녕, 익명!"
console.log(greet("홍길동")); // "안녕, 홍길동!"
console.log(greet("김철수", "hi")); // "hi, 김철수!"
비동기 관련 문법
- 자바스크립트는 비동기 작업(예: 네트워크 요청, 파일 읽기, 타이머)을 처리하기 위해 Callback, Promise, async/await를 사용합니다.
- Callback은 오래된 방식으로, 현재는 Promise와 async/await가 주로 사용됩니다.
Callback (콜백 함수)
- 비동기 작업이 완료된 후 실행할 함수를 전달하는 방식입니다. 하지만 "콜백 지옥(Callback Hell)" 문제가 발생할 수 있습니다.
예시
- 콜백 지옥
setTimeout(() => {
console.log("1단계 완료");
setTimeout(() => {
console.log("2단계 완료");
setTimeout(() => {
console.log("3단계 완료");
}, 1000);
}, 1000);
}, 1000);
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"
Promise
- Promise는 비동기 작업의 성공(resolve) 또는 실패(reject)를 처리하는 객체입니다. 콜백 지옥을 해결하는 데 유용합니다.
기본 사용
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 가정: 작업 성공
if (success) {
resolve("작업 성공!");
} else {
reject("작업 실패!");
}
}, 2000);
});
myPromise
.then(result => console.log(result)) // 성공 시 실행
.catch(error => console.error(error)); // 실패 시 실행
// 2초 후: "작업 성공!"
Promise 체이닝
const step1 = () => new Promise(resolve => {
setTimeout(() => resolve("1단계 완료"), 1000);
});
const step2 = () => new Promise(resolve => {
setTimeout(() => resolve("2단계 완료"), 1000);
});
const step3 = () => new Promise(resolve => {
setTimeout(() => resolve("3단계 완료"), 1000);
});
step1()
.then(result => {
console.log(result);
return step2();
})
.then(result => {
console.log(result);
return step3();
})
.then(result => console.log(result))
.catch(error => console.error(error));
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"
Promise.all
- 여러 Promise를 병렬로 실행합니다
Promise.all([step1(), step2(), step3()])
.then(results => console.log(results)) // 모든 Promise가 완료된 후 실행
.catch(error => console.error(error));
// 출력: ["1단계 완료", "2단계 완료", "3단계 완료"]
async/await
- async/await는 Promise를 더 간결하고 동기적으로 보이게 작성하는 문법입니다. async 함수는 항상 Promise를 반환합니다.
기본 사용
async function fetchData() {
try {
const result = await new Promise(resolve => {
setTimeout(() => resolve("데이터 가져오기 성공!"), 2000);
});
console.log(result);
} catch (error) {
console.error("에러:", error);
}
}
fetchData(); // 2초 후: "데이터 가져오기 성공!"
Promise 체이닝 대체
async function runSteps() {
try {
const result1 = await step1();
console.log(result1);
const result2 = await step2();
console.log(result2);
const result3 = await step3();
console.log(result3);
} catch (error) {
console.error(error);
}
}
runSteps();
// 출력:
// 1초 후: "1단계 완료"
// 2초 후: "2단계 완료"
// 3초 후: "3단계 완료"
병렬 실행 (Promise.all과 함께)
async function runStepsParallel() {
try {
const results = await Promise.all([step1(), step2(), step3()]);
console.log(results);
} catch (error) {
console.error(error);
}
}
runStepsParallel();
// 출력: ["1단계 완료", "2단계 완료", "3단계 완료"]
요약
- 모던 JS 문법
1. Arrow Function: 간결한 함수 정의, this 바인딩이 렉시컬 스코프
2. Spread 구문: 배열/객체 복사 및 병합
3. Optional Chaining: 안전한 속성 접근
4. Destructuring: 객체/배열에서 값 추출
5. Template Literals: 문자열 내 변수 삽입
6. Default Parameters: 함수 매개변수 기본값 설정 - 비동기 관련 문법
1. Callback: 비동기 작업의 기본, 콜백 지옥 문제
2. Promise: 성공/실패 처리, 체이닝 가능
3. async/await: Promise를 동기적으로 보이게 작성, 에러 처리를 try/catch로 관리
4. Promise.all: 여러 비동기 작업 병렬 실행
코드 이해해보기
- 첫번째 코드
// 변수 선언: let을 사용하여 재할당 가능한 문자열 변수 message를 선언하고 "Hello, World!"로 초기화
let message = "Hello, World!";
// 상수 선언: const를 사용하여 재할당 불가능한 상수 pi를 선언하고 3.14로 초기화
const pi = 3.14;
// 변수 선언: let을 사용하여 재할당 가능한 불리언 변수 isActive를 선언하고 true로 초기화
let isActive = true;
// 객체 선언: let을 사용하여 재할당 가능한 객체 변수 user를 선언
// 객체는 name과 age 속성을 가짐
let user = {
name: "Hong Gil-dong", // 속성: name은 "Hong Gil-dong" 문자열
age: 25 // 속성: age는 25 숫자
};
// 배열 선언: let을 사용하여 재할당 가능한 배열 변수 colors를 선언
// 배열은 "red", "green", "blue" 문자열 요소를 포함
let colors = ["red", "green", "blue"];
// 함수 정의: greet라는 이름의 함수를 정의, name 매개변수를 받음
function greet(name) {
// 콘솔에 "Hello, "와 name, "!"를 연결한 문자열 출력
console.log("Hello, " + name + "!");
}
// 함수 호출: greet 함수를 호출하며 "Anna"를 인자로 전달
// 콘솔에 "Hello, Anna!"가 출력됨
greet("Anna");
- 두번째 코드
// 객체 선언: let을 사용하여 재할당 가능한 객체 변수 student를 선언
let student = {
name: "Kim Yoon-sung", // 속성: name은 "Kim Yoon-sung" 문자열
major: "Computer Science", // 속성: major는 "Computer Science" 문자열
// 메소드 정의: getIntroduction이라는 함수를 속성으로 정의
getIntroduction: function() {
// 콘솔에 "My name is ", this.name, " and I study ", this.major, "."를 연결한 문자열 출력
// this는 student 객체를 참조하므로 this.name은 "Kim Yoon-sung", this.major는 "Computer Science"
console.log("My name is " + this.name + " and I study " + this.major + ".");
}
};
// 메소드 호출: student 객체의 getIntroduction 메소드를 호출
// 콘솔에 "My name is Kim Yoon-sung and I study Computer Science."가 출력됨
student.getIntroduction();
// 배열 선언: let을 사용하여 재할당 가능한 배열 변수 numbers를 선언
// 배열은 숫자 1, 2, 3, 4, 5를 요소로 포함
let numbers = [1, 2, 3, 4, 5];
// 배열 메소드 사용: push 메소드를 호출하여 배열 끝에 6을 추가
numbers.push(6);
// 콘솔 출력: numbers 배열을 출력
// push로 6이 추가되었으므로 [1, 2, 3, 4, 5, 6]이 출력됨
console.log(numbers);
- 세번째 코드
// setTimeout: 지정된 시간(밀리초) 후에 함수를 한 번 실행하는 내장 함수
// 3000ms(3초) 후에 익명 함수를 실행
setTimeout(function() {
// 3초 후에 콘솔에 메시지 출력
console.log("3초가 지났어요!");
}, 3000);
// 변수 선언: 카운트를 저장할 변수 count를 0으로 초기화
let count = 0;
// setInterval: 지정된 시간 간격(밀리초)마다 함수를 반복 실행하는 내장 함수
// 1000ms(1초)마다 익명 함수를 실행, intervalId에 interval의 ID를 저장
let intervalId = setInterval(function() {
// count를 1씩 증가
count++;
// 현재 count 값을 포함한 메시지 출력
console.log(count + "초마다 메시지가 출력됩니다.");
// count가 5 이상이면 반복 중지
if (count >= 5) {
// clearInterval: intervalId를 사용해 setInterval 반복을 중지
clearInterval(intervalId);
}
}, 1000);
// 배열 선언: fruits 배열에 문자열 요소 "apple", "banana", "cherry"를 포함
let fruits = ["apple", "banana", "cherry"];
// forEach: 배열의 각 요소에 대해 주어진 함수를 실행하는 배열 메소드
// 각 요소(fruit)를 콘솔에 출력
fruits.forEach(function(fruit) {
console.log(fruit);
// 출력:
// apple
// banana
// cherry
});
// 배열 선언: numbers 배열에 숫자 요소 1, 2, 3, 4, 5를 포함
let numbers = [1, 2, 3, 4, 5];
// map: 배열의 각 요소에 대해 주어진 함수를 적용한 결과를 새로운 배열로 반환
// 각 요소(number)를 2배로 만들어 새로운 배열 doubledNumbers에 저장
let doubledNumbers = numbers.map(function(number) {
return number * 2; // 각 숫자를 2배로 변환
});
// doubledNumbers 배열 출력: [2, 4, 6, 8, 10]
console.log(doubledNumbers);
// filter: 배열의 각 요소에 대해 주어진 조건을 만족하는 요소만 새로운 배열로 반환
// 짝수인 요소만 필터링하여 새로운 배열 evenNumbers에 저장
let evenNumbers = numbers.filter(function(number) {
return number % 2 === 0; // number가 짝수일 경우 true 반환
});
// evenNumbers 배열 출력: [2, 4]
console.log(evenNumbers);
다음과 같은 내용에 도전해봅시다.
var, let과 const의 차이점 이해하기
var | let | const | |
스코프 | 함수 스코프 | 블록 스코프 | 블록 스코프 |
재선언 | 가능 | 불가 | 불가 |
재할당 | 가능 | 가능 | 불가 |
호이스팅 | undefined | TDZ | TDZ |
권장 여부 | 비권장 | 권장 | 권장(상수용 |
Arrow Function 이해하기
- Arrow Function(화살표 함수)은 ES6에서 도입된 간결한 함수 표현식입니다. function 키워드 대신 =>를 사용하며, this 바인딩 방식이 다릅니다.
장점 | 주의점 |
간결한 문법 | this가 고정되므로 객체 메소드나 이벤트 핸들러에서 부적합할 수 있음 |
this가 렉시컬 스코프로 고정되어 예측 가능 | arguments 객체 사용 불가 |
배열 메소드(mpa, forEach 등)와 함께 사용 시 유용 |
- 예시
const obj = {
name: "홍길동",
sayHello: function() {
setTimeout(function() {
console.log(this.name); // 일반 함수: this는 setTimeout의 this (undefined)
}, 1000);
setTimeout(() => {
console.log(this.name); // Arrow Function: this는 obj (김철수)
}, 1000);
}
};
obj.sayHello();
// 출력:
// undefined
// 홍길동
룰렛 게임 완성하기
주어진 코드
<!DOCTYPE html>
<html>
<head>
<title>Roulette Game</title>
<script></script>
</head>
<body>
<div id="roulette">1</div>
<button id="stopButton">정지</button>
<script>
const values = [1, 2, 3, 4, 5, 6];
const rouletteDisplay = document.getElementById("roulette");
let intervalId = null;
let currentIndex = 0;
function startRoulette() {
intervalId = //interval 설정하기
}
document.getElementById("stopButton").addEventListener("click", () => {
clearInterval(intervalId);
alert("선택된 숫자: " + values[currentIndex]);
});
startRoulette();
</script>
</body>
</html>
완성된 코드
<!DOCTYPE html>
<html>
<head>
<title>Roulette Game</title>
<style>
#roulette { /* 룰렛 숫자가 표시될 div 요소의 스타일 */
font-size: 48px; /* 글자 크기를 48px로 설정 */
text-align: center; /* 텍스트를 가운데 정렬 */
margin: 20px; /* 외부 여백을 20px로 설정 */
padding: 20px; /* 내부 여백을 20px로 설정 */
border: 2px solid #333; /* 2px 두께의 검은색(#333) 테두리 */
width: 100px; /* div의 너비를 100px로 설정 */
margin: 20px auto; /* 상하 여백 20px, 좌우 여백 auto로 가운데 정렬 */
}
#stopButton { /* 정지 버튼의 스타일 */
display: block; /* 버튼을 블록 요소로 설정하여 한 줄 차지 */
margin: 0 auto; /* 좌우 여백 auto로 가운데 정렬 */
padding: 10px 20px; /* 상하 10px, 좌우 20px 내부 여백 */
font-size: 16px; /* 글자 크기를 16px로 설정 */
border: none; /* 테두리 제거 */
cursor: pointer; /* 마우스 커서를 포인터로 변경 (클릭 가능 표시) */
}
</style>
</head>
<body>
<!-- 룰렛 숫자가 표시될 div -->
<div id="roulette">1</div>
<!-- 룰렛을 멈추는 버튼 -->
<button id="stopButton">정지</button>
<script>
// 룰렛에 표시될 숫자 배열 (const로 선언: 재할당 불가)
const values = [1, 2, 3, 4, 5, 6];
// DOM 요소: 룰렛 숫자가 표시될 div 요소 가져오기
const rouletteDisplay = document.getElementById("roulette");
// intervalId 변수: setInterval의 ID를 저장 (let으로 선언: 재할당 필요)
let intervalId = null;
// currentIndex 변수: 현재 표시 중인 숫자의 인덱스 (let으로 선언: 재할당 필요)
let currentIndex = 0;
// startRoulette 함수: 룰렛을 시작하는 함수
function startRoulette() {
// setInterval: 100ms마다 숫자를 변경하는 타이머 설정
intervalId = setInterval(function() { // Arrow Function 대신 일반 function 사용
// 현재 인덱스를 1 증가
currentIndex = currentIndex + 1;
// 만약 currentIndex가 배열의 길이(6) 이상이 되면 0으로 초기화 (순환)
if (currentIndex >= values.length) { // % 연산 대신 if 조건문으로 변경
currentIndex = 0; // 배열의 처음으로 돌아감
}
// 룰렛 div에 현재 숫자 표시
rouletteDisplay.textContent = values[currentIndex]; // 현재 인덱스의 숫자를 div에 표시
}, 100); // 100ms 간격으로 실행 (0.1초마다 숫자 변경)
}
// stopButton 클릭 이벤트 리스너: 버튼 클릭 시 룰렛 멈춤
document.getElementById("stopButton").addEventListener("click", () => {
// clearInterval: intervalId를 사용해 setInterval 중지
clearInterval(intervalId);
// 현재 선택된 숫자를 알림창으로 표시
alert("선택된 숫자: " + values[currentIndex]);
});
// 페이지 로드 시 룰렛 자동 시작
startRoulette();
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Roulette Game</title>
<!-- 스타일 추가: 룰렛과 버튼의 시각적 효과 개선 -->
<style>
#roulette { /* 룰렛 숫자가 표시될 div 요소의 스타일 */
font-size: 48px; /* 글자 크기를 48px로 설정 */
text-align: center; /* 텍스트를 가운데 정렬 */
margin: 20px; /* 외부 여백을 20px로 설정 */
padding: 20px; /* 내부 여백을 20px로 설정 */
border: 2px solid #333; /* 2px 두께의 검은색(#333) 테두리 */
width: 100px; /* div의 너비를 100px로 설정 */
margin: 20px auto; /* 상하 여백 20px, 좌우 여백 auto로 가운데 정렬 */
}
#stopButton { /* 정지 버튼의 스타일 */
display: block; /* 버튼을 블록 요소로 설정하여 한 줄 차지 */
margin: 0 auto; /* 좌우 여백 auto로 가운데 정렬 */
padding: 10px 20px; /* 상하 10px, 좌우 20px 내부 여백 */
font-size: 16px; /* 글자 크기를 16px로 설정 */
border: none; /* 테두리 제거 */
cursor: pointer; /* 마우스 커서를 포인터로 변경 (클릭 가능 표시) */
}
</style>
</head>
<body>
<!-- 룰렛 숫자가 표시될 div -->
<div id="roulette">1</div>
<!-- 룰렛을 멈추는 버튼 -->
<button id="stopButton">정지</button>
<script>
// 룰렛에 표시될 숫자 배열 (const로 선언: 재할당 불가)
const values = [1, 2, 3, 4, 5, 6];
// DOM 요소: 룰렛 숫자가 표시될 div 요소 가져오기
const rouletteDisplay = document.getElementById("roulette");
// intervalId 변수: setInterval의 ID를 저장 (let으로 선언: 재할당 필요)
let intervalId = null;
// currentIndex 변수: 현재 표시 중인 숫자의 인덱스 (let으로 선언: 재할당 필요)
let currentIndex = 0;
// startRoulette 함수: 룰렛을 시작하는 함수
function startRoulette() {
// setInterval: 100ms마다 실행, intervalId에 저장
intervalId = setInterval(() => {
// currentIndex 증가, values 배열 길이를 초과하면 0으로 초기화 (순환)
currentIndex = (currentIndex + 1) % values.length;
// 룰렛 div에 현재 숫자 표시
rouletteDisplay.textContent = values[currentIndex];
}, 100); // 100ms 간격으로 숫자 변경 (빠르게 순환)
}
// stopButton 클릭 이벤트 리스너: 버튼 클릭 시 룰렛 멈춤
document.getElementById("stopButton").addEventListener("click", () => {
// clearInterval: intervalId를 사용해 setInterval 중지
clearInterval(intervalId);
// 현재 선택된 숫자를 알림창으로 표시
alert("선택된 숫자: " + values[currentIndex]);
});
// 페이지 로드 시 룰렛 자동 시작
startRoulette();
</script>
</body>
</html>
'ELITE HACKER Bootcamp 4th > 2주차' 카테고리의 다른 글
[2주차 TIL] KnockOn Bootcamp Javascript Part 1 (0) | 2025.04.12 |
---|---|
[2주차 TIL] KnockOn Bootcamp HTML (0) | 2025.04.12 |