1. 호출스택
- Anonymous는 가상의 전역 컨텍스트
- 함수 호출 순서대로 쌓이고 역순으로 실행됨
- 함수 실행이 완료되면 스택에서 빠짐
- LIFO 구조라서 스택이라고 불림
호출 스택 / 백그라운드 / 테스크 큐 / 이벤트 루프 / 콘솔 / 메모리 구조를 이해 해야함
> 예제
function oneMore() {
console.log('one more');
}
function run() {
console.log('run run');
setTimeout(() => {
console.log('wow');
}, 0)
new Promise((resolve) => {
resolve('hi');
})
.then(console.log);
oneMore();
}
setTimeout(run, 5000);
(console)
run run
one more
hi
wow
2. var / const / let
var: 현재는 거의 사용x, 레거시 코드 수정시 알아야함. 블록스코프를 무시하고 함수 스코프만 존중하는 특징.
const: 블록 스코프를 존중. 객체나 배열의 경우, const로 선언된 변수 자체를 재할당 할 수는 없지만, 객체 내부의 속성값 변경은 가능. 초기값 할당 필요.
let: const 와 달리 값을 자유롭게 변경 가능. const에 비해 사용빈도가 적을 수 있음.
3. 템플릿 문자열 / 객체 리터럴
> 템플릿 문자열
- `${변수명}` 형태로 문자열 삽입 가능
- 백틱 문자열은 함수 호출 기능도 있음. ex) a() == a``
> 객체 리터럴
- 변수 이름과 속성 이름이 같을 경우 sayNode: sayNode와 같이 반복해서 작성하지 않고 sayNode처럼 한 번에 합쳐서 사용할 수 있음.
- 동적 속성명을 객체 안에 직접 선언할 수 있음
- 메서드를 선언할 때 sayJS: function() { ... }에서 function 키워드를 생략하고 sayJS() { ... }처럼 간결하게 작성할 수 있음
4. 화살표 함수
> 화살표 함수의 등장 배경 및 특징
- let과 const가 variable을 거의 완벽하게 대체하는 것처럼, 화살표 함수는 function을 대체할 수 있지만 완벽하지는 않음
- 기존 function 문법보다 간결하게 코드를 작성할 수 있다는 장점.
- 특히, 중괄호 {} 다음에 바로 return이 나오는 경우 return과 중괄호 {} 를 생략할 수 있음
- 매개변수가 하나인 경우 괄호 () 도 생략할 수 있음.
- 너무많은 생략은 코드를 헷갈리게 하므로, return 값을 괄호로 묶는게 좋음
> 객체를 반환하는 경우의 주의사항
- 화살표 함수가 객체 리터럴을 반환할 때, 중괄호 {}와 return을 생략하면 JavaScript 엔진이 이를 함수의 바디로 해석할지 객체로 해석할지 혼동할 수 있음.
- 따라서 객체를 반환하는 경우에는 소괄호로 () 객체를 묶어주는것이 필수. ex) const exam = (x, y) => ({x, y})
> 화살표 함수와 기존 함수의 차이점
- 기존 function이 사라지지 않은 가장 큰 이유는 this의 존재
- function은 자신만의 this를 가지는 반면, 화살표함수는 부모 스코프의 this를 물려받음
- 이러한 this의 차이 때문에, 부모의 this를 사용해야 하는 경우 기존 function에서는 that과 같은 변수에 this를 저장하는 꼼수를 사용하기도 했음
- 하지만 화살표 함수는 자체 this를 갖지 않으므로, 부모 function의 this를 그대로 물려받아 코드가 더 간결해짐
- buttonaddEventListener와 같이 특정 HTML요소의 this를 참조해야 하는 경우, 화살표 함수를 사용하면 의도와 다르게 동작할 수 있으므로 주의해야 함.
> 사용 권장사항
- this를 사용해야하는 상황이라면 function을 사용하고, this가 필요 없다면 화살표 함수로 통일하는 것을 권장.
- React나 Vue와 같은 프레임워크에서는 this를 많이 사용하므로, function과 화살표 함수의 this동작을 확실히 구별해야 함.
5. 구조분해 할당
> 객체 구조 분해 할당
- const {a, b, d } = example 과 같이 객체 내의 속성들을 변수로 쉽게 꺼내올 수 있음
- 기존에는 const a = example.a 와 같이 속성을 개별적으로 할당해야 했지만, 구조분해 할당을 통해 코드를 더 간결하게 만들 수 있음
- 객체 구조 분해 할당 시에는 Key의 이름이 정확히 일치해야 함. 필요하다면 a: newA와 같이 새로운 변수 이름으로 변경할 수도 있음
> 배열 구조 분해 할당
- const [x, y, , , z] = array 와 같이 배열의 요소들을 변수로 할당할 수 있음
- 배열 구조 분해 할당 시에는 자릿수(인덱스)가 중요하며, 콤마(,)를 사용하여 특정 자리를 건너 뛸 수 있음
> 주의사항
- this를 사용하는 함수나 메서드의 경우, 구조분해할당을 사용하면 문제가 발생할 수 있으므로 this를 사용하는 경우에는 구조분해 할당을 피하는 것이 좋음
6. 클래스
> 클래스와 프로토 타입의 관계
- 클래스는 사실 프로토 타입의 깔끔한 문법적 표현일 뿐이며, 별도의 기능이 추가된 것이 아님. 따라서 클래스를 배우더라도 프로토타입에 대한 이해는 필수적.
> 클래스 도입 전후의 코드 비교
- 생성자 함수 및 메서드 선언: 과거에는 생성자 함수, 스태틱 메서드, 인스턴스 메서드가 각각 따로 선언되어 코드가 분리되어 있었지만, 클래스 문법에서는 이 모든 것이 하나의 클래스 블록 안에 합쳐져 훨씬 깔끔하게 그룹화되어 보임
// 1. 클래스 선언 (생성자, 인스턴스 메서드, 스태틱 메서드를 한 블록에)
class Human {
// 2. 생성자 (constructor): 인스턴스가 생성될 때 호출되는 특별한 메서드
constructor(name, age) {
this.name = name; // 인스턴스 속성
this.age = age; // 인스턴스 속성
}
// 3. 인스턴스 메서드 (클래스 내부에 바로 선언)
// Human의 모든 인스턴스가 사용할 수 있습니다.
sayName() {
console.log(`안녕하세요, 저는 ${this.name}입니다.`);
}
// 4. 스태틱 메서드 (static 키워드 사용)
// 클래스 자체에 속하며, 인스턴스에는 상속되지 않습니다.
// Human.create() 처럼 클래스 이름으로 직접 호출합니다.
static create(name, age) {
return new Human(name, age);
}
}
const person1 = new Human('김철수', 30);
person1.sayName(); // 안녕하세요, 저는 김철수입니다.
const person2 = Human.create('이영희', 25); // 스태틱 메서드 호출
person2.sayName(); // 안녕하세요, 저는 이영희입니다.
// console.log(person1.create); // undefined
- 상속 : 과거에는 Human.apply(this, arguments) 나 Object.create() 와 같은 복잡한 코드를 상속하여 구현했지만, 클래스 문법에는 class Zero extends Human과 같이 extends키워드 하나로 상속이 매우 간편해짐
// 부모 클래스
class Human {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName() {
console.log(`안녕하세요, 저는 ${this.name}입니다.`);
}
walk() {
console.log(`${this.name}이(가) 걷습니다.`);
}
}
// 자식 클래스
class Zero extends Human { // 1. extends 키워드로 상속 선언 (매우 간편!)
constructor(name, age, skill) {
// 2. super() 호출: 부모 클래스(Human)의 생성자를 호출합니다.
// 자식 클래스에서 constructor를 정의할 때는 반드시 super()를 가장 먼저 호출해야 합니다.
super(name, age);
this.skill = skill;
}
coding() {
console.log(`${this.name}이(가) ${this.skill} 코딩을 합니다.`);
}
// 부모 메서드를 오버라이딩하면서 부모 메서드를 호출할 수도 있습니다.
sayName() {
super.sayName(); // 3. super.메서드(): 부모 클래스의 메서드를 호출합니다.
console.log(`저는 개발자 ${this.name}입니다.`);
}
}
const zero = new Zero('제로초', 32, 'JavaScript');
zero.sayName(); // 안녕하세요, 저는 이름입니다. (부모 메서드 호출)
// 저는 개발자 이름입니다. (자식 메서드 추가)
zero.walk(); // 이름이(가) 걷습니다.
zero.coding(); // 이름이(가) JavaScript 코딩을 합니다.
console.log(zero instanceof Zero); // true
console.log(zero instanceof Human); // true
- 부모 함수 호출: super() 키워드를 사용하여 부모 클래스의 생성자나 메서드를 쉽게 호출할 수 있게 되어 코드가 직관적
7. Promise, async/await
> Promise (프로미스)
- Promise는 실행은 되었지만 결과를 아직 반환하지 않은 객체
- resolve를 호출하면 성공, reject를 호출하면 실패를 의미.
- Promise는 콜백과 달리 코드를 분리하여 나중에 원하는 시점에 then이나 catch를 붙여 값을 받을 수 있어 코드의 가독성과 유연성이 높음
- Node.js 생태계는 콜백에서 Promise로 전환되고 있으며, 대부분의 함수가 Promise를 지원하는 방향으로 바뀌고 있음
- Promise를 사용하면 콜백 지옥(Callback Hell)을 막을 수 있음
- Promise.all과 Promise.allSettled를 사용하여 여러 Promise를 동시에 실행할 수 있으며, Promise.allSettled는 실패한 Promise만 추려낼 수 있음
- resolve는 성공 시, reject는 실패 시 호출되며, resolve는 then으로, reject는 catch로 연결됨
> Async/Await (에이싱크/어웨이트)
- async/await은 Promise의 문법을 더 간결하게 만든 것으로, then 체인을 줄여 코드를 더 짧게 만듦
- await은 Promise의 then 역할을 하며, 실행 순서는 오른쪽에서 왼쪽으로 진행
- await을 사용하려면 항상 함수에 async 키워드를 붙여야 함
- 최근에는 Top-level await이 도입되어 async 함수로 감싸지 않아도 await을 사용할 수 있게 되어 있음.
- async 함수에서 반환되는 값은 항상 Promise로 반환되므로, then으로 받거나 await으로 받을 수 있음
- async/await 사용 시 Promise의 실패(reject)를 처리하려면 try-catch 문으로 감싸야 함
- for await...of 문은 Promise 배열을 반복할 때 유용하게 사용
> 실무에서의 프로토타입 이해
- 다른 사람의 라이브러리나 프레임워크 코드를 분석할 때 필요하다고 강조
- 객체지향 프로그래밍을 하는 개발자들의 코드를 이해하기 위해서는 프로토타입에 대한 지식이 필수적
- 결론적으로, 함수형 프로그래밍이든 객체지향 프로그래밍이든 모든 기본 문법을 골고루 알아두는 것이 원활한 프로그래밍과 코드 분석에 중요
8. Map, Set, WeakMap, WeakSet
> Map 객체
- Map은 일반 객체 리터럴과 유사하게 키-값 쌍을 저장하지만, 키에 문자열이나 심볼 외에 객체도 사용할 수 있다는 점이 다름
- new Map()으로 생성, set()으로 값을 추가하고 get()으로 값을 가져옴
- 키와 값이 1대1로 매칭되는 구조, 키로 객체를 사용할 경우 참조값이 같아야 동일한 값을 가져올 수 있음.
- 크기는 size 속성으로 확인, for...of 루프나 forEach()를 사용하여 반복
- has()로 존재 여부를 확인, delete()로 삭제, clear()로 모든 요소를 삭제
- 일반 객체 리터럴보다 Map을 사용하는 것이 더 편리함
> Set 객체
- Set은 일반 배열 리터럴과 유사하지만, 중복을 허용하지 않는다는 특징이 있음.
- new Set()으로 생성하며, add()로 값을 추가
- Map과 유사하게 size 속성, has() 메서드, for...of 루프, forEach(), delete(), clear() 등을 사용
- 중복을 제거하는 데 유용하며, 배열의 중복을 제거할 때 new Set()을 사용한 후 Array.from()으로 다시 배열로 변환하는 등 사용
> WeakMap 및 WeakSet
- WeakMap과 WeakSet은 가비지 컬렉션(메모리 정리)이 잘 된다는 특징이 있음
- WeakMap은 키가 객체일 때 해당 객체가 더 이상 참조되지 않으면 WeakMap에 저장된 키-값 쌍도 함께 가비지 컬렉션됨. 일반 Map은 키 객체가 널이 되어도 Map이 해당 객체를 참조하고 있으면 가비지 컬렉션되지 않음
- WeakMap은 객체를 수정하지 않으면서 부가적인 정보를 추가할 때 유용하게 사용
- WeakSet은 실전적인 예제를 찾기 어렵다
9. 옵셔널 체이닝
> 널 병합 연산자 (??)
- 용도: 주로 null과 undefined 값을 구분하기 위해 사용
- 기존 || (또는) 연산자와의 차이점:
- || 연산자는 false, 0, 빈 문자열(''), null, undefined 등 모든 falsy 값일 때 뒤의 값으로 넘어감.
- ?? 연산자는 오직 null과 undefined일 때만 뒤의 값으로 넘어감.
- 예를 들어, 변수 count가 0일 때 count || 123은 123을 반환하지만, count ?? 123은 0을 반환함. 이는 0이 null이나 undefined가 아니기 때문
- 활용: 0이나 빈 문자열과 같은 falsy 값이지만 유효한 값으로 처리하고 싶을 때 유용.
> 옵셔널 체이닝 연산자 (?.)
- 용도: 객체의 속성에 접근하거나 함수를 호출할 때, 해당 속성이나 함수가 null 또는 undefined일 경우 발생할 수 있는 오류(Cannot read properties of null/undefined)를 방지.
- 오류 메시지 해석: Cannot read properties of D of C와 같은 오류 메시지가 떴을 때, D가 null이 아니라 C가 null 또는 undefined라는 의미.
- 사용법:
- 속성 접근 시: 객체?.속성. 예를 들어, c?.d는 c가 존재하면 c.d를 반환하고, c가 null 또는 undefined이면 undefined를 반환함.
- 함수 호출 시: 객체?.함수().
- 배열 접근 시: 배열?.인덱스 (예: c?.). 이때 ?. 뒤에 .을 붙임 (?.가 하나의 연산자).
- 장점: if 문을 사용하여 속성 존재 여부를 일일이 확인하는 코드를 줄여 코드의 가독성을 높이고 간결하게 만듦.
- 주의사항: 모든 점(. ) 앞에 ?.를 붙이는 것은 가독성을 해칠 수 있으므로, 확실하지 않은 경우에만 사용. 타입스크립트를 사용하면 ?.를 붙여야 할 때를 더 명확하게 알 수 있음.
> 함께 사용하기
- 널 병합 연산자와 옵셔널 체이닝 연산자를 함께 사용하여, 특정 속성이 null 또는 undefined일 경우 기본값을 제공. 예를 들어, c?.d ?? 123은 c가 존재하고 d가 null이나 undefined가 아니면 c.d를 반환하고, c가 없거나 d가 null/undefined이면 123을 반환.
10. 프론트엔드 자바스크립트
- Axios를 사용한 비동기 요청: axios 라이브러리를 사용하여 서버에 GET 및 POST 요청을 보내는 방법. 비동기 요청의 특성상 실패 가능성을 염두에 두고 catch를 사용하여 에러를 처리하는 것이 중요함. 또한, axios.get이 Promise를 지원하므로 then과 catch를 붙일 수 있으며, async/await 문법으로도 변환하여 사용할 수 있음.
- POST 요청과 데이터 전송: POST 요청은 로그인이나 회원가입처럼 서버에 데이터를 함께 보낼 때 주로 사용.
- FormData 객체 활용: 이미지나 파일 업로드 시 FormData 객체를 사용하여 데이터를 전송
- URI 인코딩 및 디코딩: 주소창에 한글과 같은 아스키 코드 외의 문자가 포함될 경우 발생할 수 있는 서버 오류를 방지하기 위해 encodeURIComponent 함수로 인코딩하고, 서버에서 decodeURIComponent 함수로 다시 디코딩. 특히, URL이 아닌 URI이며, 이는 파일 위치보다는 자원의 위치를 가리키는 개념으로 변화했기 때문임
- HTML 태그에 데이터 저장 (Dataset): HTML 태그에 data- 속성을 사용하여 데이터를 저장. 이는 공개되어도 되는 데이터를 저장할 때 유용하며, 자바스크립트에서 dataset 속성을 통해 접근하고 값을 변경할 수 있음
'FE > JavaScript' 카테고리의 다른 글
| [#0 Node] 기초개념 (3) | 2025.07.26 |
|---|---|
| [26장] 모던 자바스크립트 Deep Dive - ES6 함수의 추가 기능 (0) | 2025.01.12 |
| [24장] 모던 자바스크립트 Deep Dive - 클로저 (1) | 2024.12.27 |
| [21장] 모던 자바스크립트 Deep Dive - 빌트인 객체 (0) | 2024.12.11 |
| [17장] 모던 자바스크립트 Deep Dive - 생성자 함수에 의한 객체 생성 (1) | 2024.12.02 |