객체를 더 간결하게 작성하는 방법입니다.
변수명과 객체의 키(key) 이름이 같을 때, 축약 표기법을 사용할 수 있습니다.
// ❌ 기존 방식: 중복되는 이름
const name = "Alice";
const age = 25;
const city = "Seoul";
const user1 = {
name: name,
age: age,
city: city
};
// ✅ Shorthand Property Names: 간결한 표현
const user2 = {
name, // name: name
age, // age: age
city // city: city
};
console.log(user2); // { name: 'Alice', age: 25, city: 'Seoul' }
// API 응답 데이터 생성
function createResponse(status, data, message) {
return {
status, // status: status
data, // data: data
message, // message: message
timestamp: Date.now()
};
}
const response = createResponse(200, { id: 1 }, "Success");
console.log(response);
// {
// status: 200,
// data: { id: 1 },
// message: 'Success',
// timestamp: 1234567890
// }
// 함수에서 객체 반환
function getUserInfo() {
const id = 1;
const name = "Bob";
const email = "bob@example.com";
return { id, name, email }; // 간결한 반환
}
console.log(getUserInfo());
// { id: 1, name: 'Bob', email: 'bob@example.com' }
객체나 배열에서 값을 추출하는 강력한 문법입니다.
구조 분해 할당(Destructuring Assignment)은 객체의 속성이나 배열의 요소를 개별 변수로 쉽게 추출할 수 있게 해줍니다.
const user = {
name: "Alice",
age: 25,
city: "Seoul"
};
// ❌ 기존 방식
const name = user.name;
const age = user.age;
const city = user.city;
// ✅ 구조 분해 할당
const { name, age, city } = user;
console.log(name, age, city); // "Alice" 25 "Seoul"
const user = { name: "Bob", age: 30 };
// 다른 이름의 변수로 할당
const { name: userName, age: userAge } = user;
console.log(userName, userAge); // "Bob" 30
const user = { name: "Charlie" };
// age가 없으면 기본값 사용
const { name, age = 20, city = "Unknown" } = user;
console.log(name, age, city); // "Charlie" 20 "Unknown"
const user = {
name: "David",
address: {
city: "Seoul",
country: "Korea"
}
};
const {
name,
address: { city, country }
} = user;
console.log(name, city, country); // "David" "Seoul" "Korea"
const colors = ["red", "green", "blue"];
// ❌ 기존 방식
const color1 = colors[0];
const color2 = colors[1];
const color3 = colors[2];
// ✅ 구조 분해 할당
const [first, second, third] = colors;
console.log(first, second, third); // "red" "green" "blue"
const numbers = [1, 2, 3, 4, 5];
// 필요한 요소만 추출
const [first, , third, , fifth] = numbers;
console.log(first, third, fifth); // 1 3 5
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
const [a = 1, b = 2, c = 3] = [10, 20];
console.log(a, b, c); // 10 20 3
// 1. 함수 매개변수에서 구조 분해
function printUser({ name, age, city = "Seoul" }) {
console.log(`${name}은 ${age}살이고 ${city}에 살고 있습니다.`);
}
printUser({ name: "Alice", age: 25 });
// "Alice은 25살이고 Seoul에 살고 있습니다."
// 2. 배열 교환
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
// 3. 함수에서 여러 값 반환
function getUser() {
return {
id: 1,
name: "Bob",
email: "bob@example.com"
};
}
const { id, name, email } = getUser();
console.log(id, name, email); // 1 "Bob" "bob@example.com"
객체나 배열을 펼쳐서 복사하거나 병합하는 연산자입니다.
Spread 연산자(...)는 배열이나 객체의 요소를 개별 요소로 펼칩니다.
// 배열 복사
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
arr2.push(4);
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]
// 배열 병합
const arr3 = [1, 2];
const arr4 = [3, 4];
const merged = [...arr3, ...arr4];
console.log(merged); // [1, 2, 3, 4]
// 배열 중간에 요소 추가
const arr5 = [1, 2, 5];
const arr6 = [1, 2, 3, 4, 5];
console.log([...arr3, 3, 4, ...arr5]); // [1, 2, 3, 4, 1, 2, 5]
// 객체 복사
const obj1 = { name: "Alice", age: 25 };
const obj2 = { ...obj1 };
obj2.age = 30;
console.log(obj1); // { name: "Alice", age: 25 }
console.log(obj2); // { name: "Alice", age: 30 }
// 객체 병합
const user = { name: "Bob" };
const details = { age: 30, city: "Seoul" };
const fullUser = { ...user, ...details };
console.log(fullUser);
// { name: "Bob", age: 30, city: "Seoul" }
// 속성 덮어쓰기
const original = { x: 1, y: 2 };
const updated = { ...original, y: 3, z: 4 };
console.log(updated); // { x: 1, y: 3, z: 4 }
// 1 depth는 깊은 복사
const obj1 = { key: "1" };
const obj2 = { key2: "2" };
const merge = { ...obj1, ...obj2 };
merge.key2 = 100;
console.log(obj2); // { key2: "2" } ✅ 원본 유지
console.log(merge); // { key: "1", key2: 100 }
// 2 depth부터는 얕은 복사
const original = {
user: {
name: "Alice",
age: 25
}
};
const copy = { ...original };
copy.user.age = 30; // 중첩 객체는 참조 유지
console.log(original.user.age); // 30 ⚠️ 원본도 변경됨!
// 1. 함수 인자로 배열 전달
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6
// 2. 배열 합치기
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];
const combined = [...arr1, ...arr2, ...arr3];
console.log(combined); // [1, 2, 3, 4, 5, 6]
// 3. 불변성 유지하며 상태 업데이트
const state = {
user: { name: "Alice" },
count: 0
};
const newState = {
...state,
count: state.count + 1
};
console.log(newState); // { user: { name: "Alice" }, count: 1 }
함수 매개변수의 기본값을 설정하는 방법입니다.
함수 호출 시 인자가 전달되지 않거나 undefined일 때 사용할 기본값을 설정합니다.
// ❌ 기존 방식
function greet(name) {
name = name || "Guest";
console.log(`Hello, ${name}!`);
}
// ✅ Default Parameters
function greet(name = "Guest") {
console.log(`Hello, ${name}!`);
}
greet(); // "Hello, Guest!"
greet("Alice"); // "Hello, Alice!"
greet(undefined); // "Hello, Guest!"
greet(null); // "Hello, null!" ⚠️ null은 기본값 X
// 1. 여러 기본값 설정
function createUser(name = "Anonymous", age = 0, city = "Unknown") {
return { name, age, city };
}
console.log(createUser());
// { name: "Anonymous", age: 0, city: "Unknown" }
console.log(createUser("Alice", 25));
// { name: "Alice", age: 25, city: "Unknown" }
// 2. 표현식을 기본값으로 사용
function getDefaultName() {
return "Guest_" + Math.random().toString(36).substr(2, 9);
}
function greet(name = getDefaultName()) {
console.log(`Hello, ${name}!`);
}
greet(); // "Hello, Guest_a8xc9k2!"
// 3. 이전 매개변수 참조
function createRectangle(width = 10, height = width * 2) {
return { width, height };
}
console.log(createRectangle()); // { width: 10, height: 20 }
console.log(createRectangle(5)); // { width: 5, height: 10 }
console.log(createRectangle(5, 8)); // { width: 5, height: 8 }
// 4. 객체 구조 분해와 함께 사용
function printUser({ name = "Anonymous", age = 0 } = {}) {
console.log(`${name} (${age}세)`);
}
printUser(); // "Anonymous (0세)"
printUser({ name: "Alice" }); // "Alice (0세)"
printUser({ name: "Bob", age: 30 }); // "Bob (30세)"
조건문을 한 줄로 표현하는 연산자입니다.
삼항 연산자는 조건 ? 참일때값 : 거짓일때값 형태로 사용합니다.
// ❌ 기존 방식
let message;
const isLoggedIn = true;
if (isLoggedIn) {
message = "Welcome!";
} else {
message = "Please log in";
}
// ✅ Ternary Operator
const message = isLoggedIn ? "Welcome!" : "Please log in";
console.log(message); // "Welcome!"
// 1. 변수 할당
const age = 20;
const isAdult = age >= 18 ? true : false;
console.log(isAdult); // true
// 2. 함수 반환값
function getGreeting(hour) {
return hour < 12 ? "Good morning! 🌅" : "Good afternoon! 🌞";
}
console.log(getGreeting(10)); // "Good morning! 🌅"
console.log(getGreeting(15)); // "Good afternoon! 🌞"
// 3. 중첩 삼항 연산자 (권장하지 않음)
const score = 85;
const grade = score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" : "F";
console.log(grade); // "B"
// 4. JSX에서 조건부 렌더링 (React)
const isLoading = false;
const content = isLoading ? <Spinner /> : <Content />;
// 5. 배열 메서드와 함께
const numbers = [1, 2, 3, 4, 5];
const evenOrOdd = numbers.map(num =>
num % 2 === 0 ? "짝수" : "홀수"
);
console.log(evenOrOdd); // ["홀수", "짝수", "홀수", "짝수", "홀수"]
// ❌ 너무 복잡한 중첩은 가독성 저하
const result = condition1 ?
(condition2 ? value1 : value2) :
(condition3 ? value3 : value4);
// ✅ 복잡한 로직은 if-else 사용
if (condition1) {
if (condition2) {
result = value1;
} else {
result = value2;
}
} else if (condition3) {
result = value3;
} else {
result = value4;
}
문자열을 더 편리하게 다루는 방법입니다.
템플릿 리터럴은 백틱(`)을 사용하여 문자열 안에 변수나 표현식을 쉽게 삽입할 수 있습니다.
const name = "Alice";
const age = 25;
// ❌ 기존 방식
const message1 = "Hello, " + name + "! You are " + age + " years old.";
// ✅ Template Literals
const message2 = `Hello, ${name}! You are ${age} years old.`;
console.log(message2); // "Hello, Alice! You are 25 years old."
// 1. 여러 줄 문자열
const multiLine = `
This is line 1
This is line 2
This is line 3
`;
console.log(multiLine);
// 2. 표현식 사용
const a = 10;
const b = 20;
console.log(`${a} + ${b} = ${a + b}`); // "10 + 20 = 30"
// 3. 함수 호출
function getPrice(price) {
return price.toLocaleString();
}
const product = "Laptop";
const price = 1500000;
console.log(`${product}: ${getPrice(price)}원`);
// "Laptop: 1,500,000원"
// 4. 중첩 템플릿
const isLoggedIn = true;
const userName = "Bob";
const greeting = `Hello, ${isLoggedIn ? `${userName}!` : "Guest!"}`;
console.log(greeting); // "Hello, Bob!"
// 5. HTML 템플릿 생성
const user = { name: "Charlie", email: "charlie@example.com" };
const html = `
<div class="user-card">
<h2>${user.name}</h2>
<p>${user.email}</p>
</div>
`;
console.log(html);
// 커스텀 템플릿 처리
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return `${result}${str}<strong>${values[i] || ""}</strong>`;
}, "");
}
const name = "Alice";
const age = 25;
const result = highlight`Name: ${name}, Age: ${age}`;
console.log(result);
// "Name: <strong>Alice</strong>, Age: <strong>25</strong>"
안전하게 중첩된 속성에 접근하는 방법입니다.
Optional Chaining(?.)은 객체의 속성이 존재하지 않을 때 에러 대신 undefined를 반환합니다.
const user = {
name: "Alice",
address: {
city: "Seoul"
}
};
// ❌ 기존 방식: 에러 발생 가능
// console.log(user.profile.bio); // TypeError!
// ❌ 기존 방식: 복잡한 체크
if (user && user.profile && user.profile.bio) {
console.log(user.profile.bio);
}
// ✅ Optional Chaining
console.log(user.profile?.bio); // undefined (에러 없음!)
console.log(user.address?.city); // "Seoul"
// 1. 깊은 중첩 객체 접근
const person = {
name: "Bob",
contact: {
email: "bob@example.com",
phone: {
mobile: "010-1234-5678"
}
}
};
// 안전한 접근
console.log(person.contact?.phone?.mobile); // "010-1234-5678"
console.log(person.contact?.address?.city); // undefined
// 2. 배열 요소 접근
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
];
console.log(users[0]?.name); // "Alice"
console.log(users[5]?.name); // undefined
console.log(users?.[0]?.age); // 25
// 3. 함수 호출
const obj = {
greet: () => "Hello!"
};
console.log(obj.greet?.()); // "Hello!"
console.log(obj.sayBye?.()); // undefined (에러 없음)
// 4. Nullish Coalescing과 함께 사용
const city = person.address?.city ?? "Unknown";
console.log(city); // "Unknown"
// API 응답 처리
const apiResponse = {
data: {
user: {
profile: {
name: "Charlie",
avatar: {
url: "https://example.com/avatar.jpg"
}
}
}
}
};
// 안전한 데이터 추출
const avatarUrl = apiResponse?.data?.user?.profile?.avatar?.url;
console.log(avatarUrl); // "https://example.com/avatar.jpg"
// 존재하지 않는 데이터도 안전하게
const bio = apiResponse?.data?.user?.profile?.bio ?? "No bio available";
console.log(bio); // "No bio available"
null/undefined를 확실하게 체크하는 연산자입니다.
Nullish Coalescing Operator(??)는 왼쪽 값이 null 또는 undefined일 때만 오른쪽 값을 반환합니다.
// || 연산자: falsy 값 모두 체크
const value1 = 0 || "default";
console.log(value1); // "default" (0은 falsy)
const value2 = "" || "default";
console.log(value2); // "default" (빈 문자열은 falsy)
// ?? 연산자: null/undefined만 체크
const value3 = 0 ?? "default";
console.log(value3); // 0 ✅
const value4 = "" ?? "default";
console.log(value4); // "" ✅
const value5 = null ?? "default";
console.log(value5); // "default"
const value6 = undefined ?? "default";
console.log(value6); // "default"
// 1. 설정값 기본값 처리
const config = {
timeout: 0, // 0은 유효한 값
retries: null, // null은 설정되지 않음
cache: false, // false는 유효한 값
endpoint: "" // 빈 문자열도 유효한 값
};
const timeout = config.timeout ?? 3000;
const retries = config.retries ?? 3;
const cache = config.cache ?? true;
const endpoint = config.endpoint ?? "https://api.example.com";
console.log(timeout); // 0 (설정값 유지)
console.log(retries); // 3 (기본값 사용)
console.log(cache); // false (설정값 유지)
console.log(endpoint); // "" (빈 문자열 유지)
// 2. 사용자 입력 처리
function createUser(name, age, score) {
return {
name: name ?? "Anonymous",
age: age ?? 0, // 0살도 유효
score: score ?? 0 // 0점도 유효
};
}
console.log(createUser("Alice", 0, 0));
// { name: "Alice", age: 0, score: 0 } ✅
console.log(createUser(null, null, null));
// { name: "Anonymous", age: 0, score: 0 }
// 3. Optional Chaining과 함께
const user = {
profile: {
bio: "" // 빈 문자열도 유효한 bio
}
};
const bio = user.profile?.bio ?? "No bio provided";
console.log(bio); // "" (빈 문자열 유지)
const location = user.profile?.location ?? "Unknown";
console.log(location); // "Unknown"
| 값 | \| 결과 | ?? 결과 |
|---|---|---|
0 | 오른쪽 값 | 0 (왼쪽 값) |
"" | 오른쪽 값 | "" (왼쪽 값) |
false | 오른쪽 값 | false (왼쪽 값) |
null | 오른쪽 값 | 오른쪽 값 |
undefined | 오른쪽 값 | 오른쪽 값 |
NaN | 오른쪽 값 | NaN (왼쪽 값) |
// ✅ ?? 사용: 0, "", false가 유효한 값인 경우
const count = userInput ?? 10; // 0도 허용
const name = userName ?? "Guest"; // 빈 문자열도 허용
const enabled = isEnabled ?? true; // false도 허용
// ✅ || 사용: falsy 값을 모두 기본값으로 대체하고 싶은 경우
const displayName = userName || "Guest"; // 빈 문자열은 "Guest"로
모든 문법을 활용한 실무 코드 예시입니다.
// API 응답 데이터 생성
function createUserResponse(
userId,
userData = {},
options = {}
) {
// Destructuring + Default Parameters
const {
name = "Anonymous",
age = 0,
email = null,
address = {}
} = userData;
const {
includeTimestamp = true,
format = "json"
} = options;
// Shorthand Property Names + Spread Syntax
const response = {
userId,
user: {
name,
age,
email: email ?? "No email provided",
...address
},
...(includeTimestamp && { timestamp: Date.now() })
};
// Template Literals + Ternary Operator
return format === "json"
? JSON.stringify(response, null, 2)
: `User: ${name} (${age}세)`;
}
// 사용 예시
const result = createUserResponse(
123,
{
name: "Alice",
age: 25,
address: { city: "Seoul", country: "Korea" }
},
{ includeTimestamp: true }
);
console.log(result);
/*
{
"userId": 123,
"user": {
"name": "Alice",
"age": 25,
"email": "No email provided",
"city": "Seoul",
"country": "Korea"
},
"timestamp": 1234567890
}
*/
class ShoppingCart {
constructor() {
this.items = [];
}
// 상품 추가
addItem(product) {
const {
id,
name,
price = 0,
quantity = 1,
discount = 0
} = product;
// Optional Chaining + Nullish Coalescing
const existingItem = this.items.find(item => item.id === id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ id, name, price, quantity, discount });
}
return this;
}
// 총 금액 계산
getTotal() {
return this.items.reduce((total, item) => {
const { price, quantity, discount = 0 } = item;
const itemTotal = price * quantity * (1 - discount / 100);
return total + itemTotal;
}, 0);
}
// 장바구니 요약
getSummary() {
const total = this.getTotal();
const itemCount = this.items.reduce((sum, item) =>
sum + item.quantity, 0
);
return `
🛒 장바구니 요약
----------------
총 상품 수: ${itemCount}개
총 금액: ${total.toLocaleString()}원
상품 목록:
${this.items.map(item =>
`- ${item.name}: ${item.quantity}개 (${item.price.toLocaleString()}원)`
).join('\n ')}
`;
}
}
// 사용 예시
const cart = new ShoppingCart();
cart
.addItem({ id: 1, name: "노트북", price: 1500000, quantity: 1, discount: 10 })
.addItem({ id: 2, name: "마우스", price: 50000, quantity: 2 })
.addItem({ id: 3, name: "키보드", price: 100000, quantity: 1 });
console.log(cart.getSummary());
/*
🛒 장바구니 요약
----------------
총 상품 수: 4개
총 금액: 1,600,000원
상품 목록:
- 노트북: 1개 (1,500,000원)
- 마우스: 2개 (50,000원)
- 키보드: 1개 (100,000원)
*/
// API 데이터 정규화
function normalizeApiData(apiResponse) {
// Optional Chaining으로 안전한 접근
const data = apiResponse?.data ?? [];
return data.map(item => {
// Destructuring + Default Values
const {
id,
name = "Unknown",
email = null,
isActive = false,
metadata = {}
} = item;
// Spread + Shorthand
return {
id,
name,
email: email ?? "no-email@example.com",
status: isActive ? "active" : "inactive",
...metadata,
// Template Literals
displayName: `${name} (${id})`
};
});
}
// 사용 예시
const apiResponse = {
data: [
{ id: 1, name: "Alice", email: "alice@example.com", isActive: true },
{ id: 2, name: "Bob", isActive: false },
{ id: 3, email: null, metadata: { role: "admin" } }
]
};
const normalized = normalizeApiData(apiResponse);
console.log(normalized);
/*
[
{
id: 1,
name: "Alice",
email: "alice@example.com",
status: "active",
displayName: "Alice (1)"
},
{
id: 2,
name: "Bob",
email: "no-email@example.com",
status: "inactive",
displayName: "Bob (2)"
},
{
id: 3,
name: "Unknown",
email: "no-email@example.com",
status: "inactive",
role: "admin",
displayName: "Unknown (3)"
}
]
*/
class ConfigManager {
constructor(defaultConfig = {}) {
// Spread로 기본 설정 복사
this.config = {
theme: "light",
language: "ko",
notifications: true,
autoSave: false,
timeout: 30000,
...defaultConfig
};
}
// 설정 값 가져오기
get(key, fallback = null) {
// Optional Chaining + Nullish Coalescing
return this.config?.[key] ?? fallback;
}
// 설정 업데이트
update(updates = {}) {
// Spread로 불변성 유지
this.config = {
...this.config,
...updates
};
return this;
}
// 설정 초기화
reset(keys = []) {
if (keys.length === 0) {
// 전체 초기화
this.config = {
theme: "light",
language: "ko",
notifications: true,
autoSave: false,
timeout: 30000
};
} else {
// 특정 키만 초기화
keys.forEach(key => {
const defaults = {
theme: "light",
language: "ko",
notifications: true,
autoSave: false,
timeout: 30000
};
this.config[key] = defaults[key];
});
}
return this;
}
// 설정 출력
toString() {
const { theme, language, notifications, autoSave, timeout } = this.config;
return `
⚙️ 현재 설정
--------------
테마: ${theme}
언어: ${language}
알림: ${notifications ? "켜짐" : "꺼짐"}
자동 저장: ${autoSave ? "켜짐" : "꺼짐"}
타임아웃: ${timeout}ms
`.trim();
}
}
// 사용 예시
const config = new ConfigManager({ theme: "dark" });
console.log(config.toString());
/*
⚙️ 현재 설정
--------------
테마: dark
언어: ko
알림: 켜짐
자동 저장: 꺼짐
타임아웃: 30000ms
*/
config.update({ autoSave: true, timeout: 60000 });
console.log(config.get("autoSave")); // true
console.log(config.get("maxRetries", 3)); // 3 (fallback)
| 문법 | 기호 | 용도 | 예시 |
|---|---|---|---|
| Shorthand Property | - | 객체 속성 축약 | { name, age } |
| Destructuring | { } [ ] | 구조 분해 할당 | const { name } = obj |
| Spread | ... | 배열/객체 펼치기 | { ...obj } |
| Default Parameters | = | 기본값 설정 | function(x = 0) |
| Ternary | ? : | 삼항 연산자 | a ? b : c |
| Template Literals | ` | 문자열 템플릿 | `Hello ${name}` |
| Optional Chaining | ?. | 안전한 접근 | obj?.prop |
| Nullish Coalescing | ?? | null/undefined 체크 | value ?? default |
// ❌ 읽기 어려운 코드
function processUser(user) {
var name;
if (user && user.profile && user.profile.name) {
name = user.profile.name;
} else {
name = "Anonymous";
}
var age;
if (user && user.profile && user.profile.age) {
age = user.profile.age;
} else {
age = 0;
}
return { name: name, age: age };
}
// ✅ 깔끔한 코드
function processUser(user) {
const name = user?.profile?.name ?? "Anonymous";
const age = user?.profile?.age ?? 0;
return { name, age };
}
// ❌ 원본 수정
function updateUser(user, updates) {
user.name = updates.name;
user.age = updates.age;
return user; // 원본이 수정됨!
}
// ✅ 새 객체 반환
function updateUser(user, updates) {
return { ...user, ...updates };
}
// ❌ 에러 발생 가능
function getCity(user) {
return user.address.city; // user.address가 없으면 에러!
}
// ✅ 안전한 접근
function getCity(user) {
return user?.address?.city ?? "Unknown";
}
// 여러 문법을 조합한 실용적인 함수
function createApiRequest({
endpoint,
method = "GET",
headers = {},
body = null,
timeout = 5000
} = {}) {
// Template Literals + Optional Chaining + Nullish Coalescing
const url = endpoint ?? throw new Error("Endpoint is required");
// Spread + Shorthand
const config = {
method,
headers: {
"Content-Type": "application/json",
...headers
},
...(body && { body: JSON.stringify(body) }),
timeout
};
// Ternary Operator + Template Literals
return `
📡 API Request
--------------
URL: ${url}
Method: ${method}
Timeout: ${timeout}ms
Has Body: ${body ? "Yes" : "No"}
`.trim();
}
// 사용
console.log(createApiRequest({
endpoint: "/api/users",
method: "POST",
body: { name: "Alice" }
}));