본문 바로가기
JavaScript/React

React 프로그래밍을 위한 javascript 기본

by curious week 2025. 3. 21.

ES6+ 문법 

let과 const

ES6에서는 var 대신 블록 스코프를 가지는 let과 불변 변수인 const가 추가됨.

let x = 10;
x = 20;  // 가능

const y = 30;
y = 40;  // 에러 (const는 재할당 불가능)

let → 재할당 가능
const → 재할당 불가능


화살표 함수 (()=>{})

ES6에서 함수 선언을 간결하게 표현하는 방법.

// 일반 함수 표현식
function add(a, b) {
  return a + b;
}

// 화살표 함수
const add = (a, b) => a + b;

console.log(add(2, 3)); // 5

특징

  • return을 생략 가능 (한 줄 코드일 때)
  • this가 기존 함수와 다르게 동작 (lexical this)
const obj = {
  value: 10,
  normalFunc: function () {
    console.log(this.value);  // this → obj
  },
  arrowFunc: () => {
    console.log(this.value);  // this → 상위 스코프 (window or undefined)
  }
};

obj.normalFunc(); // 10
obj.arrowFunc();  // undefined (또는 window 객체 값)

템플릿 리터럴 (`${}`)

문자열을 더 쉽게 조작할 수 있도록 도와주는 문법.

const name = "희성";
const age = 25;

// 기존 문자열 결합 방식
console.log("이름: " + name + ", 나이: " + age);

// 템플릿 리터럴 사용
console.log(`이름: ${name}, 나이: ${age}`);

백틱(```)을 사용하여 여러 줄 문자열도 가능

const multiLine = `첫 번째 줄
두 번째 줄
세 번째 줄`;

console.log(multiLine);

객체 구조 분해 할당 (Destructuring)

객체에서 필요한 속성만 쉽게 꺼내서 사용할 수 있음.

const person = {
  name: "희성",
  age: 25,
  job: "개발자"
};

// 기존 방식
console.log(person.name);
console.log(person.age);

// 구조 분해 할당
const { name, age } = person;
console.log(name); // "희성"
console.log(age);  // 25

주의점

  • 객체의 속성 이름과 같은 변수 이름을 사용해야 함.
  • const { job: occupation } = person; 처럼 **별칭(Alias)**을 사용할 수도 있음.
const { job: occupation } = person;
console.log(occupation); // "개발자"

배열 구조 분해 할당

배열에서도 특정 요소를 쉽게 꺼낼 수 있음.

const numbers = [1, 2, 3];

// 기존 방식
console.log(numbers[0], numbers[1]);

// 구조 분해 할당
const [first, second] = numbers;
console.log(first);  // 1
console.log(second); // 2

필요 없는 값은 빈칸으로 건너뛰기

const [, , third] = numbers;
console.log(third); // 3

나머지 값들을 배열로 저장할 수도 있음.

const [first, ...rest] = numbers;
console.log(rest); // [2, 3]

스프레드 연산자 (...)

스프레드 연산자는 배열, 객체를 펼치거나 복사하는 데 사용됨.

배열에서 사용

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];

console.log(arr2); // [1, 2, 3, 4, 5]

배열 복사

const copy = [...arr1]; // 깊은 복사
console.log(copy); // [1, 2, 3]

배열 결합

const arr3 = [4, 5, 6];
const combined = [...arr1, ...arr3];

console.log(combined); // [1, 2, 3, 4, 5, 6]

객체에서 사용

const user = { name: "희성", age: 25 };
const updatedUser = { ...user, job: "개발자" };

console.log(updatedUser); 
// { name: "희성", age: 25, job: "개발자" }

객체 복사

const copyUser = { ...user };
console.log(copyUser); // { name: "희성", age: 25 }

객체 병합

const extraInfo = { job: "개발자", location: "서울" };
const fullUser = { ...user, ...extraInfo };

console.log(fullUser); 
// { name: "희성", age: 25, job: "개발자", location: "서울" }

속성 덮어쓰기

const updatedUser2 = { ...user, age: 30 };
console.log(updatedUser2); 
// { name: "희성", age: 30 } (원래 age=25였지만 덮어씌워짐)

 

디폴트 파라미터 (Default Parameters)

함수의 매개변수에 기본값을 설정할 수 있음.

function sum(a = 0, b = 0) {
  return a + b;
}

console.log(sum(5, 10)); // 15
console.log(sum(5));     // 5 (b가 없으므로 기본값 0 사용)
console.log(sum());      // 0 (a와 b 모두 기본값 사용)

객체를 매개변수로 전달할 때도 기본값을 설정 가능

function greet(user = { name: "Guest" }) {
  console.log(`Hello, ${user.name}!`);
}

greet({ name: "희성" }); // Hello, 희성!
greet();                 // Hello, Guest!

주의: undefined일 때만 기본값 적용, null은 적용되지 않음.

console.log(sum(undefined, 5)); // 5  (기본값 적용)
console.log(sum(null, 5));      // null + 5 = 5 (null은 기본값 적용 X)

Promise (프로미스)

비동기 작업을 처리하는 객체. resolve(성공), reject(실패) 상태를 가짐.

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("데이터 로드 완료!");
    }, 2000);
  });
};

fetchData()
  .then((result) => console.log(result)) // "데이터 로드 완료!" (2초 후)
  .catch((error) => console.error(error));

resolve() → 성공, reject() → 실패

const fetchDataWithError = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("데이터 로드 실패!");
    }, 2000);
  });
};

fetchDataWithError()
  .then((result) => console.log(result))
  .catch((error) => console.error(error)); // "데이터 로드 실패!" (2초 후)

여러 개의 Promise 실행 (Promise.all, Promise.race)

const p1 = new Promise((res) => setTimeout(() => res("첫 번째 완료"), 1000));
const p2 = new Promise((res) => setTimeout(() => res("두 번째 완료"), 2000));

Promise.all([p1, p2]).then(console.log); 
// ["첫 번째 완료", "두 번째 완료"] (2초 후)

Promise.race([p1, p2]).then(console.log);
// "첫 번째 완료" (1초 후, 가장 먼저 실행된 프로미스 반환)

async/await

Promise를 더 쉽게 다룰 수 있도록 하는 ES8 문법.

const fetchData = async () => {
  return "데이터 로드 완료!";
};

fetchData().then(console.log); // "데이터 로드 완료!"

await를 사용하여 Promise 결과를 기다릴 수 있음

const fetchWithDelay = () => {
  return new Promise((resolve) => {
    setTimeout(() => resolve("데이터 로드 완료!"), 2000);
  });
};

const getData = async () => {
  console.log("로딩 중...");
  const result = await fetchWithDelay(); // 2초 대기
  console.log(result);
};

getData(); 
// "로딩 중..." (즉시 실행)
// "데이터 로드 완료!" (2초 후 실행)

예외 처리 (try-catch)

const fetchWithError = () => {
  return new Promise((_, reject) => {
    setTimeout(() => reject("데이터 로드 실패!"), 2000);
  });
};

const getDataWithErrorHandling = async () => {
  try {
    console.log("로딩 중...");
    const result = await fetchWithError(); // 2초 대기
    console.log(result);
  } catch (error) {
    console.error("에러 발생:", error);
  }
};

getDataWithErrorHandling(); 
// "로딩 중..." (즉시 실행)
// "에러 발생: 데이터 로드 실패!" (2초 후 실행)

함수형 프로그래밍(Functional Programming, FP)은 순수 함수(Pure Function)와 불변성(Immutability)을 중시하는 프로그래밍 패러다임입니다.
ES6+에서는 map(), filter(), reduce() 같은 배열 메서드를 활용하여 함수형 스타일의 코드를 작성할 수 있습니다.

배열의 주요 메서드

map()

배열의 각 요소를 변형하여 새로운 배열을 반환합니다.

const numbers = [1, 2, 3, 4];

// 모든 요소를 제곱
const squared = numbers.map(num => num ** 2);

console.log(squared); // [1, 4, 9, 16]

기존 배열(numbers)을 변경하지 않고 새로운 배열을 생성함 → 불변성 유지!


filter()

조건을 만족하는 요소만 추려 새로운 배열을 반환합니다.

const numbers = [1, 2, 3, 4, 5];

// 짝수만 필터링
const evenNumbers = numbers.filter(num => num % 2 === 0);

console.log(evenNumbers); // [2, 4]

reduce()

배열의 요소들을 하나의 값으로 축약할 때 사용합니다.

const numbers = [1, 2, 3, 4, 5];

// 모든 숫자의 합 구하기
const sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(sum); // 15

초깃값(0)을 설정해야 하며, acc(누적값)가 반복되면서 최종 값이 됨.


forEach()

배열의 각 요소를 순회하며 반복 작업을 수행합니다. 하지만 새로운 배열을 반환하지 않습니다!

const numbers = [1, 2, 3];

numbers.forEach(num => console.log(num * 2)); 
// 2, 4, 6 (출력만 할 뿐 새로운 배열을 생성하지 않음)

map()과 다르게 새로운 배열을 반환하지 않으므로, 값을 변환하려면 map()을 사용하는 것이 좋습니다.


find()

조건을 만족하는 첫 번째 요소를 반환합니다.

const users = [
  { id: 1, name: "희성" },
  { id: 2, name: "지민" },
  { id: 3, name: "태형" }
];

const user = users.find(user => user.id === 2);

console.log(user); // { id: 2, name: "지민" }

여러 개가 조건을 만족하더라도 첫 번째 요소만 반환합니다.


some()

배열에 하나라도 조건을 만족하는 요소가 있으면 true 반환.

const numbers = [1, 3, 5, 7];

// 짝수가 하나라도 있는가?
const hasEven = numbers.some(num => num % 2 === 0);

console.log(hasEven); // false (짝수가 없음)

every()

배열의 모든 요소가 조건을 만족해야 true 반환.

const numbers = [2, 4, 6, 8];

// 모든 요소가 짝수인가?
const allEven = numbers.every(num => num % 2 === 0);

console.log(allEven); // true (모든 요소가 짝수)

불변성 (Immutability)

함수형 프로그래밍에서는 원본 데이터를 변경하지 않고 새로운 데이터를 생성하는 방식을 권장합니다.

Object.assign()을 활용한 불변 객체 생성

const user = { name: "희성", age: 25 };

// 새로운 속성 추가 (원본 변경 X)
const updatedUser = Object.assign({}, user, { job: "개발자" });

console.log(updatedUser); // { name: "희성", age: 25, job: "개발자" }
console.log(user);        // { name: "희성", age: 25 } (원본 변경 X)

스프레드 연산자 (...)를 활용한 불변 객체 생성

const user = { name: "희성", age: 25 };

// 새로운 속성 추가 (불변성 유지)
const updatedUser = { ...user, job: "개발자" };

console.log(updatedUser); // { name: "희성", age: 25, job: "개발자" }
console.log(user);        // { name: "희성", age: 25 } (원본 변경 X)

객체 속성을 변경할 때도 새로운 객체를 만들어야 함.

const updatedUser2 = { ...user, age: 30 };

console.log(updatedUser2); // { name: "희성", age: 30 }
console.log(user);         // { name: "희성", age: 25 } (원본 변경 X)

스프레드 연산자로 불변 배열 생성

const numbers = [1, 2, 3];

// 새로운 요소 추가 (불변성 유지)
const newNumbers = [...numbers, 4];

console.log(newNumbers); // [1, 2, 3, 4]
console.log(numbers);    // [1, 2, 3] (원본 변경 X)

특정 요소를 수정할 때도 새로운 배열을 만들어야 함.

const updatedNumbers = numbers.map(num => (num === 2 ? 20 : num));

console.log(updatedNumbers); // [1, 20, 3]
console.log(numbers);        // [1, 2, 3] (원본 변경 X)

비동기 프로그래밍

비동기 프로그래밍은 시간이 걸리는 작업(예: API 호출, 파일 읽기, 타이머 등)을 블로킹 없이 실행하는 방식.

1️⃣ setTimeout()

일정 시간이 지난 후 한 번만 실행되는 함수

console.log("시작");

setTimeout(() => {
  console.log("3초 후 실행됨");
}, 3000);

console.log("끝");

실행 순서:

시작
끝
(3초 후) 3초 후 실행됨

2️⃣ setInterval()

일정 시간 간격으로 반복 실행되는 함수

let count = 0;

const interval = setInterval(() => {
  count++;
  console.log(`실행 횟수: ${count}`);

  if (count === 5) {
    clearInterval(interval); // 5번 실행 후 종료
  }
}, 1000);

실행 결과:

실행 횟수: 1
실행 횟수: 2
실행 횟수: 3
실행 횟수: 4
실행 횟수: 5
(이후 중지)

3️⃣ Promise

비동기 작업을 처리하는 객체로, 성공(resolve()) 또는 실패(reject()) 상태를 가짐.

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("데이터 로드 완료!");
    }, 2000);
  });
};

fetchData()
  .then(result => console.log(result)) // 2초 후 "데이터 로드 완료!"
  .catch(error => console.error(error));

resolve() → 성공하면 .then() 실행
reject() → 실패하면 .catch() 실행

const fetchDataWithError = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("데이터 로드 실패!");
    }, 2000);
  });
};

fetchDataWithError()
  .then(result => console.log(result))
  .catch(error => console.error("에러 발생:", error)); // "에러 발생: 데이터 로드 실패!" (2초 후)

4️⃣ async/await

Promise를 더 쉽게 다룰 수 있도록 하는 문법

const fetchData = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve("데이터 로드 완료!"), 2000);
  });
};

const getData = async () => {
  console.log("로딩 중...");
  const result = await fetchData(); // 2초 기다림
  console.log(result);
};

getData();
// "로딩 중..." 즉시 출력
// (2초 후) "데이터 로드 완료!"

await을 사용하면 비동기 함수가 실행될 때까지 기다림
try-catch를 활용한 에러 처리 가능

const fetchDataWithError = () => {
  return new Promise((_, reject) => {
    setTimeout(() => reject("데이터 로드 실패!"), 2000);
  });
};

const getDataWithErrorHandling = async () => {
  try {
    console.log("로딩 중...");
    const result = await fetchDataWithError(); // 2초 대기
    console.log(result);
  } catch (error) {
    console.error("에러 발생:", error);
  }
};

getDataWithErrorHandling();
// "로딩 중..." 즉시 출력
// (2초 후) "에러 발생: 데이터 로드 실패!"

5️⃣ fetch() API

비동기적으로 서버에서 데이터를 가져오는 함수 (내장 HTTP 요청)

fetch("https://jsonplaceholder.typicode.com/posts/1")
  .then(response => response.json()) // JSON 변환
  .then(data => console.log(data))
  .catch(error => console.error("에러 발생:", error));

response.json() → 서버 응답을 JSON으로 변환
.catch() → 네트워크 오류 처리 가능

async/await으로 fetch() 사용하기

const fetchPost = async () => {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("에러 발생:", error);
  }
};

fetchPost();

await fetch(url) → 서버 응답을 기다림
await response.json() → JSON 변환도 기다려야 함