비동기 작업이 최종적으로 성공했는지 실패했는지를 나타내는 객체.
🖥️ javascript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("data"), 1000);
});
}
function processData(data) {
return `${data} processed`;
}
fetchData()
.then(processData) // 모듈화된 함수 호출
.then(result => console.log(result))
.catch(error => console.error(error));
🖥️ javascript
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("data"), 1000);
});
}
function processData(data) {
return `${data} processed`;
}
async function main() {
try {
const data = await fetchData();
const result = processData(data); // 모듈화된 함수 호출
console.log(result);
} catch (error) {
console.error(error);
}
}
main();
Promise와 then()/catch()
: 함수 호출 체인을 분리하고 재사용성을 확보하는 데 적합하다.
하지만 체인 길이가 길어지거나 복잡한 로직을 포함하면
모듈화의 장점이 가려질 수 있다.
async/await
: 코드 흐름이 간결하고 직관적이므로 함수 단위로 분리하기 용이하다.
가독성과 유지보수 측면에서 더 유리하며, 복잡한 비동기 작업에서도 코드의 명확한 모듈화 가능.
➡️ 단순한 비동기 작업이라면 Promise와 .then()도 충분히 모듈화가 가능하다. 그러나 가독성과 복잡한 작업의 처리가 필요한 경우, async/await이 모듈화와 유지보수 측면에서 더 적합하다.
클래스 문법이 프로토타입 체인을 제거하거나 대체하지는 않는다.
이는 프로토타입 기반의 문법 위에 동작하는 문법적 설탕(Syntactic Sugar)이라고 보면 된다. 즉, 프로토타입 기반의 동작을 간결하고 직관적으로 표현하도록 설계된 문법.
🖥️ javascript
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`);
};
const dog = new Animal("Dog");
dog.speak(); // Dog makes a noise.
🖥️ javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
const dog = new Animal("Dog");
dog.speak(); // Dog makes a noise.
ES6 클래스에서는 진정한 의미의 캡슐화와 데이터 보호(즉, private 필드) 기능이 없었으나, 이후 추가된 private 필드 문법(ES2022, #으로 선언)을 통해 가능해졌다.
➡️ 캡슐화와 데이터 보호는 이후 추가된 private 필드 문법으로 가능하며, 상속은 ES6 클래스의 핵심 기능 중 하나이다.
🖥️ javascript
class Example {
#privateField = 0; // private 필드
increment() {
this.#privateField++;
}
getPrivateField() {
return this.#privateField;
}
}
const example = new Example();
console.log(example.#privateField); // Error: Private field '#privateField' must be declared in an enclosing class
example.increment();
console.log(example.getPrivateField()); // 1
필드는 클래스의 상태를 저장하는 변수. ES6 클래스에는 필드 선언이 포함되지 않았으나, 이후 public, private 필드 문법이 도입됨.
🖥️ javascript
class Person {
name = "John"; // public field
#age = 30; // private field
}
메소드는 클래스의 동작(로직, behavior)을 정의하는 함수이다. 클래스 메소드는 프로토타입에 연결되며, 모든 인스턴스가 공유한다.
🖥️ javascript
class Calculator {
add(a, b) {
return a + b;
}
}
const calc = new Calculator();
console.log(calc.add(2, 3)); // 5
클래스는 객체를 생성하기 위한 템플릿이며, new 키워드를 사용하여 인스턴스를 생성. 클래스를 사용하여 새로운 객체를 생성하는 과정을 인스턴스화라고 하며, 생성된 객체는 클래스의 구조와 동작을 가진다.
🖥️ javascript
class Animal {
constructor(name) {
this.name = name;
}
}
const cat = new Animal("Cat"); // Animal의 인스턴스
console.log(cat.name); // "Cat"
getter와 setter는 객체의 프로퍼티에 접근하거나 값을 설정하는 간접적인 방법을 제공하며, 이를 통해 데이터 검증, 로깅 등의 추가 로직을 구현할 수 있다.
🖥️ javascript
class User {
constructor(name) {
this._name = name; // 내부적으로 사용하는 변수
}
get name() {
return this._name;
}
set name(newName) {
if (newName.length < 3) {
console.log("Name must be at least 3 characters.");
} else {
this._name = newName;
}
}
}
const user = new User("Alice");
console.log(user.name); // Alice
user.name = "Jo"; // Name must be at least 3 characters.
user.name = "John";
console.log(user.name); // John
➡️ JavaScript 클래스는 프로토타입 기반의 동작을 더 간결하게 표현하며, 상속, 캡슐화, 데이터 보호 등의 객체지향 프로그래밍 패턴을 지원한다. 최신 문법인 private 필드와 getter/setter를 활용하면 더욱 구조적이고 안정적인 코드를 작성할 수 있다.
과거에 브라우저는 보안을 위해 same origin policy를 강제했고
(도메인이 다르면 접근 불가) 다른 도메인에서 리소스에 접근할 수 없는 불편함 때문에 CORS 정책이 탄생했다.
CORS(Cross-Origin Resource Sharing, 교차 출처 리소스 공유)은
다른 소스 간에 리소스 요청을 제어할수 있는 정책으로,
simple request와 pre-flight request의 두 가지 경우로 나뉜다.
Simple Request는 CORS 정책에서 추가적인 확인 절차 없이 허용되는 요청 유형이다. 브라우저가 pre-flight 요청 없이 서버로 바로 요청을 보냅니다.
Simple Request로 간주되기 위해 다음 조건을 모두 만족해야 한다.
🖥️ javascript
fetch('https://example.com/api/data', {
method: 'GET',
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
위 요청은 GET 메서드를 사용하며, 사용자 정의 헤더가 없으므로 Simple Request로 간주됨.
Pre-flight Request는 브라우저가 서버로 요청을 보내기 전에
OPTIONS 메서드를 사용하여 요청의 안전성을 확인하는 과정이다.
다음 조건 중 하나라도 충족하면 Pre-flight Request가 발생.
작동 방식
🖥️ javascript
fetch('https://example.com/api/resource', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
위 요청은 다음 조건을 충족하므로 Pre-flight Request가 발생한다.
Pre-flight Request의 실제 흐름
🖥️ http
OPTIONS /api/resource HTTP/1.1
Host: example.com
Origin: https://your-website.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
🖥️ http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://your-website.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
🖥️ http
PUT /api/resource HTTP/1.1
Host: example.com
Origin: https://your-website.com
Authorization: Bearer token
Content-Type: application/json
{"key": "value"}