상세 컨텐츠

본문 제목

[React.js 1팀] 2장. 자바스크립트 실전 - 구조 분해 할당 ~ 비동기 처리

24-25/React.js 1

by mingging17 2024. 10. 4. 10:00

본문

728x90

1. 구조 분해 할당

 자바스크립트에서 객체와 배열은 가장 자주 쓰이는 자료 구조이다. 이때 구조 분해 할당(Destructuring Assignment)은 배열 및 객체 안에 있는 요소를 해체한 다음, 변수에 그 값을 저장할 때 사용할 수 있다. 따라서 구조 분해 할당은 객체나 배열에 저장된 데이터 중 일부를 분해하여 개별 변수에 담아야 할 때 쓰이며 이 외에도 함수의 매개변수가 많거나 코드를 간결하게 정리할 때 등 다양한 상황에서 활용이 가능하다.

 

 

1-(1) 배열의 구조 분해 할당

 가장 먼저 알아볼 것은 배열의 요소를 개별 변수에 할당하여 코드를 간결하게 만드는 방법이다. number 배열 안에 1, 2, 3이라는 값이 저장되어 있을 때, a, b, c 변수에 number 인덱스 값을 저장하여 a, b, c 변수만으로 1, 2, 3이라는 값을 출력할 수 있다. 이때 구조 분해 할당을 활용하면 단 세 줄만으로도 a, b, c에 값을 할당하여 1, 2, 3 값을 출력할 수 있다.

// 구조 분해 할당 코드 전
let number = [1, 2, 3];
let a = number[0];
let b = number[1];
let c = number[2];
console.log(a, b, c);

// 구조 분해 할당 코드 후
let number = [1, 2, 3];
let [a, b, c] = number;
console.log(a, b, c);

 

 만일 let [a, b, c] = number; 대신 let [a, b] = number; 만 입력하더라도 오류가 발생하지 않으며, 반대로 배열의 길이보다 할당할 변수의 개수가 더 많아도 오류가 발생하지 않는다. 후자의 경우 배열 길이를 초과한 변수에 undefined가 할당된다.

 

1-(2) 객체의 구조 분해 할당

 이번에는 배열에서 구조 분해 할당하는 법을 알아보자. 2개의 프로퍼티가 있는 객체를 생성하고 구조 분해 할당으로 프로퍼티의 값을 변수에 할당하게 되면 배열과 같이 할당된 값을 그대로 출력하게 된다. 객체는 구조 분해 할당할 때 데이터 저장 순이 아니라 key 순으로 저장되므로, 변수 값이 어떻게 변하더라도 이덕우, 25가 출력하게 된다.

let person = {
    name: "이덕우",
    age: 25
}
let {name, age} = perseon;
console.log(name, age);

 

1-(3) 함수의 매개변수가 객체일 때 구조 분해 할당하기

 함수에서도 구조 분해 할당이 가능하다. 다음과 같이  함수 매개변수로 전달된 객체에서 필요한 프로퍼티만 구조 분해 할당을 할 수 있다. 실행하게 되면, 매개변수 name, age에 각각 구조 분해 할당하여 이덕우, 25의 값을 출력한다. 이때, 콜론(:)을 활용하면 name: n 처럼 구조 분해 할당과 동시에 변수 이름도 변경할 수 있다.

function info({ name: n, age: a,}) {
    console.log(n, a);
}

let person = {
    name: "이덕우",
    age: 25,
};

info(person);

 

 

 


2. 스프레드 연산자와 rest 매개 변수

 스프레드 연산자는 반복이 가능한 객체에서 값을 개별 요소로 분리할 수 있으며, 반대로 rest 매개 변수를 활용하면 개별 요소를 다시 배열로 묶을 수 있다. 스프레드(spread) 연산자와 rest 매개변수 모두 '...' 기호로 표기된다. 스프레드 연산자는 전개 연산자라고도 불리며 rest 매개 변수는 '나머지 매개변수'라고 부를 수 있다.

 

 

2-(1) 스프레드 연산자와 배열, 객체

 스프레드 연산자를 이해하기 위해, 먼저 활용 방법을 살펴보자. 다음과 같이 스프레드 연산자를 활용할 수 있다. 스프레드 연산자를 사용하게 되면 개별 요소로 분리되어 저장이 된다. 그러나 '...' 을 붙이지 않고 A를 B에 그대로 넣게 되면 배열 자체가 요소로 추가돼 B는 [[1, 2, 3], 4, 5, 6]으로 저장된다.

let A = [1, 2, 3];
let B = [...A, 4, 5, 6];

console.log(B);

 

 객체에서 스프레드 연산자를 쓸 때도 동일한 방식으로 활용하면 된다. 이와 같이 스프레드 연산자는 프로퍼티나 배열을 개별 요소로 취급하여 자연스럽게 다른 변수에 포함시켜주는 역할을 한다.

let objA = {
  a: 1,
  b: 2
};

let objB = {
  ...objA,
  c: 3,
  d: 4
};

console.log(objB);

 

2-(2) 스프레드 연산자와 함수

 스프레드 연산자는 함수를 호출할 때도 이용할 수 있다. 이때 앞서 배운 매개변수에서 구조 분해 할당과 스프레드 연산자를 구분해야 한다. 전자의 경우 함수를 호출할 때 전달되는 인수가 1개이며 그 값이 객체라는 특징이 있다. 그러나 스프레드 연산자는 인수가 1개가 아니라 여러 개로 나뉘어 전달되므로 매개변수를 여러 개 선언해야 한다.

function func(a, b, c) {
    console.log(a, b, c);
}

let number = [1, 2, 3];
func(...number);

 

2-(3) rest 매개 변수

 스프레드 연산자를 사용할 때와 마찬가지로 rest 매개 변수를 사용할 때 매개변수로 사용할 변수 이름 앞에 '...'을 붙인다. 그러면 rest 매개변수는 함수에 전달한 인수들을 순차적으로 배열에 저장하게 된다. 다음과 같은 코드를 분석해보면, 1이 one 매개변수에 할당되고, 나머지 2, 3, 4는 배열로 묶여서 ...three에 할당된다. 이때 ...three가 one 보다 앞에 놓이게 되면 1, 2, 3, 4가 배열로 묶여서 전부 ...three에 할당되므로 반드시 rest 매개 변수는 마지막에 선언해야 한다.

function func(one, ...three) {
    console.log(one);
    console.log(three);
}

func(1, 2, 3, 4);

 

 

 

 


3. 배열과 메서드

 자바스크립트에서는 배열을 쉽게 다룰 수 있도록 여러 메서드가 제공된다. 그렇기 때문에 '배열 메서드'라고 이름이 붙여졌다. 이번 파트에서는 요소 추가 및 삭제 메서드부터 변형 메서드까지 여러 메서드의 종류와 활용법을 기술하였다.

 

 

3-(1) 요소의 추가 및 삭제 메서드

 배열 요소를 추가하거나 수정 또는 삭제할 수 있는 메서드를 알아보자.

 

- push

: 배열 에 요소를 추가하고 새로운 길이를 반환하는 메서드. 콤마를 이용해서 여러 개 인수를 배열에 추가할 수 있다.

let food = ["밥", "국"];
const newLength = food.push("게란말이", "김치");

console.log(food); // ["밥", "국", "계란말이", "김치"]가 출력된다
console.log(`새로운 배열의 길이: ${newLength}`); // 새로운 배열의 길이는 4로 출력된다

 

- pop

: 배열 맨 요소를 제거하고 제거한 요소를 반환하는 메서드. 빈 배열에서 pop을 사용하면 undefined를 반환한다.

let food = ["밥", "국", "계란말이"];
const removedItem = food.pop(); 

console.log(removedItem); // 삭제할 요소, 계란말이가 출력된다
console.log(food); // ["밥", "국"]이 출력된다

 

- shift

: 배열 맨 요소를 제거하고, 제거한 요소를 반환하는 메서드. pop 메서드와 반대되는 역할을 수행한다.

let food = ["밥", "국", "계란말이"];
const removedItem = food.shift();

console.log(removedItem); // 삭제할 요소, 밥이 출력된다
console.log(food); // ["국", "계란말이"]가 출력된다

 

- unshift

: 배열 맨 요소를 추가하고, 새 배열의 길이를 반환하는 메서드. push 메서드와 반대되는 역할을 수행한다.

let food = ["밥", "국", "계란말이"];
const newLength = food.unshift("김치");

console.log(food); // ["김치", "밥", "국", "계란말이"]가 출력된다
console.log(`새로운 배열의 길이: ${newLength}`); // 새로운 배열의 길이는 4로 출력된다

 

- slice

: 기존 배열에서 특정 범위를 잘라 새로운 배열을 반환하는 메서드. 그러나 값이 반환될 뿐 원본 배열은 수정되지 않는다. 배열에서 활용하는 slice의 기본 형태는 배열이름.slice(start, end); 이다. start는 시작을, end는 끝나는 범위를 지정한다. 만일 지정 범위가 음수일 경우, 배열 맨 끝부터 음수의 절댓값만큼 잘라낸 배열을 반환한다.

const a = [1, 2, 3];
const sliced = a.slice(0, 2);

console.log(a); // 원본 배열은 여전히 [1,2,3] 값을 출력한다
console.log(sliced); // [1, 2] 값이 출력된다

 

- concat

: 서로 다른 배열을 이어서 새로운 배열로 반환하는 메서드. 이 역시 원본 배열을 수정하는 것이 아니다. 배열 뿐만 아니라 객체도 전달할 수 있다. 이때 배열은 요소를 이어붙이고, 객체는 하나의 요소로 인식해서 삽입된다. 

 

3-(2) 순회 메서드

- forEach

: 배열의 모든 요소에 순서대로 접근해 특정 동작을 수행하는 메서드. 함수를 인수로 사용하며, 인수로 전달한 콜백 함수가 정의한 대로 요소가 동작된다. 이때 콜백 함수는 'item'(현재 순회하는 배열 요소), 'idx'(현재 순회하는 배열 요소의 인덱스), 'arr'(순회 중인 배열) 와 같은 3개의 매개변수를 사용하게 된다. 만일 arr 배열 안 요소가 3개라면 함수 cb는 총 3번 실행하게 된다.

function cb(item, index, array) {
}

arr.forEach(cb);

 

 추가로, forEach로 전달되는 콜백 함수는 화살표 함수로 간략하게 표현할 수 있다.

arr.forEach((item, idx) => {});

 

3-(3) 탐색 메서드

 특정 조건을 만족하는 요소를 찾아내는 메서드 알아보자.

- indexOf
: 찾으려는 요소의 인덱스를 반환하는 메서드. 두번째 인수는 생략할 수 있으며 생략 시 0번째 인덱스부터 탐색하게 된다. 만일 음수로 두번째 인수를 지정하게 된다면 탐색 위치는 배열 맨 부터 시작된다. 만일 찾으려는 요소가 배열에 없다면 -1을 반환하게 되며, '탐색 시작 인덱스 번호' 값이 배열의 길이보다 크거나 같은 경우에도 -1을 반환한다. indexOf는 비교 연산자 '==='를 사용하므로 괄호 안에 들어가는 자료형이 다르거나 객체를 적게 되면 이 역시 -1 값을 반환하게 된다.

arr.indexOf(찾으려는 요소, 탐색 시작 인덱스 번호);


- includes
: 배열에 특정 요소가 있는지 판별하는 메서드. indexOf와 인수가 동일하며 사용법도 동일하다. 하지만  인덱스를 알려주는 indexOf와 달리 인수로 전달한 요소가 배열에 있다면 true를, 그렇지 않다면 false를 반환한다. 

arr.includes(찾으려는 요소, 탐색 시작 인덱스 번호);


- findIndex
: 찾으려는 요소의 인덱스 번호를 찾아 반환하는 메서드. indexOf와 하는 일이 같아 보이지만, 인수로 콜백 함수를 전달한다는 점에서 다르다. 또한 findIndex는 indexOf와 달리 판별 함수를 이용해 배열에서 조건과 일치하는 객체 요소를 찾아낼 수 있다.

arr.findIndex( callback(item, index, array) );


- find
: findeIndex와 같이 인수로 판별 함수를 전달하고 배열에서 이를 만족하는 요소를 찾는 메서드. 이때 인덱스가 아닌 요소를 반환한다.

arr.find((item)=> item.name === "이덕성");


- filter
: 조건을 만족하는 요소만 모아 새로운 배열로 반환하는 메서드. 

arr.filter( callback(item, index, arr) );
arr.filter((item)=>item.name === "덕성");


3-(4) 변형 메서드

 배열을 변형하거나 요소를 재정렬할 때 쓰이는 변형 메서드에 대해 알아보자.

- map
: 각 요소에 대한 함수 호출 결과를 모아 새로운 배열을 만들어서 반환하는 메서드. 콜백 함수를 인수로 전달하며, item, index, array가 매개변수로 제공된다.

arr.map( callback(item, index, array) ); //map 메서드 기본형
let arr = [1, 2, 3, 4];
let newArr = arr.map((item) => item * 5);

console.log(newArr); // [5, 10, 15, 20]가 출력된다


 위와 같이 map 메서드를 활용해서 배열의 모든 요소에 5를 곱한 새로운 배열을 만들 수 있다.

- sort
: 요소를 정렬할 때 사용하는 메서드. 다음과 같이 문자로 이루어진 배열을 사전순, 오름차순으로 정렬할 수 있다. 

let arr = ["b", "a", "c"];

arr.sort();

console.log(arr); // ["a", "b", "c"]가 출력된다

 

 

 또한 sort 메서드는 하나의 콜백 함수를 인수로 전달하게 되는데,  이 함수를 비교 함수로 사용 시 반환값에 따라 정렬 방식을 달리 할 수 있다. 다음 함수는 오름차순으로 배열할 때 쓰이는 비교 함수이다. a와 b를 비교하였을 때 a가 더 크다면 양수 1을 반환하여 a가 b 뒤로 가게 되며 반대의 경우 -1을 반환하고 a가 b 앞으로 오게 된다. a와 b가 동일하면 자리를 바꾸지 않는다.

function compare(a, b) {
  if (a > b) {
    return 1;
  } else if (a < b) {
    return -1;
  } else {
    return 0;
  }
}


- join
: 배열 요소를 모두 연결해서 하나의 문자열로 반환하는 메서드. 분리 기호로 사용하는 구분자(separator)를 인수로 전달하고 생략 시 콤마(,)를 활용한다.

let arr = ["안녕", "나는", "이덕성"];

console.log(arr.join()); // 안녕,나는,이덕성이 출력된다
console.log(arr.join(" ")); // 안녕 나는 이덕성이 출력된다


- reduce
: 배열 요소를 모두 순회하면서 인수로 제공한 함수를 실행하고 하나의 결과값만 반환하는 메서드. acc는 누산기라는 뜻으로 이전 함수의 호출 결과를 저장하는 매개변수이며 initial이 acc의 초깃값으로 설정된다.

arr.reduce( ( acc, item, index, array ) => {
  (...)
}, initial );
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((acc, item) => acc + item, 5);

console.log(result);


위와 같은 상황에서 initial 자리에 5을 넣으면 0+1+2+3+4+5+5이 되어 20 값이 출력된다.

 

 

 

 


4. Date 객체와 날짜

 자바스크립트에서는 날짜와 시간과 관련된 기능이 추가된 Date 객체가 있다. new 키워드로 Date 객체를 생성할 수 있으며 쉽게 현재 날짜와 시간이 저장된 Date 객체를 반환할 수 있다. 이때 Date 객체는 협정 세계시라 불리는 UTC를 기준으로 동작하며 한국 표준시와 비교했을 때 9시간 느린 것을 확인할 수 있다.  그럼 이제부터  Date 객체에 대해 더 자세히 알아보자.

 

4-(1) 원하는 날짜로 Date 객체 생성 및 수정

 Date 객체 생성자에 날짜를 인수로 전달하면 해당 날짜를 기준으로 Date 객체를 만들어 반환하게 된다. "2024-09-29/00:00:00"로 설정할 수도 있지만, year, month, date, hours, minutes, seconds, milliseconds 순서로 원하는 숫자를 적어서 Date 객체를 생성할 수도 있다. 이때 주의할 점은, Date 객체는 월의 시작을 0으로 보기 때문에 만일 9월을 출력하고 싶으면 8을 입력해야 한다.

let date1 = new Date(2024, 8, 29, 0, 0, 0, 0);

console.log("1:", date1);

 

 추가로, getTime 메서드를 이용하면 Date 객체에 저장된 날짜를 타임 스탬프로 변환해서 반환할 수 있다. 이때 얻은 타임 스탬프값을 인수로 전달하면 적절한 날짜를 반환할 수 있다.

let timeStamp = date.getTime();

 

- getFullYear

- getMonth

- getDate

- getDay

- getHours, getMinutes, getSeconds, getMillisenconds

 

위 4개의 메서드는 차례대로 연도, 월, 일, 요일을 반환한다. 마지막 줄 메서드의 경우 각기 시간, 분, 초, 밀리초를 반환하게 된다. 여기서 주의할 점이 있다. getMonth의 경우는 0부터 11까지 반환한다. 표기는 1월부터 12월까지 그대로 표현되지만 반환되는 숫자는 표기 숫자에서 -1을 해야 하는 점을 잊지 말자. getDay의 경우 0부터 6으로 표현하는 요일을 반환하게 되는데 일요일 0 부터 시작되며 6은 토요일이다.

 

 이제 저장된 날짜 요소를 수정할 수 있는 메서드에 대해 알아보자.

 

- setFullYear

- setMonth

- setDate

- setDay

- setHours, setMinutes, setSeconds

 

 get에서 set으로 바뀌었다고 생각하면 외우기 좀 더 쉽다.  앞에서 배운 것을 활용하여 Date 객체 요일을 설정하고 수정해보는 코드는 다음과 같다.

let date = new Date(2024, 9, 29);
console.log(date.getDate()); // 일(日) 반환

date.setDate(30);
console.log(date.getDate()); // 수정된 일(日) 반환

 

4-(2) Date 객체 출력하기

 현재 저장된 시간을 다양한 종류의 문자열로 반환하는 메서드에 대해 알아보자.

 

- toString

: 현재 저장된 시간을 문자열로 반환하는 메서드.

- toDateString

: 시간을 제외한 현재의 날짜를 출력하는 메서드.

- toLocaleString, toLocaleDateString

: 객체에 저장된 데이터를 현재 우리가 위치한 곳의 시간대에 맞게 변환하여 반환하는 메서드. toLocaleDateString은 toLocaleString과 다르게 날짜만 반환한다. 아래 코드를 입력하게 되면 '2024. 9. 29. 오후 8:00:00' ,'2024. 9. 29.' 이 출력된다.

const today = new Date(2024, 8, 29, 20);

console.log(today.toLocaleString());
console.log(today.toLocaleDateString());

 

 

  Date 객체를 활용하면 월 단위로 달력을 이동하는 기능이나 해당하는 날짜만 필터링 하는 기능 등 다양한 기능을 구현할 수 있다. 실무에서 자주 활용되기도 하고 시간과 관련한 코드를 짤 때 유용하기 때문에 위에 언급한 Date 객체를 직접 써보며 숙지해 두는 것이 좋다.

 

 

 


5. 비동기 처리

 개념만 보자면, 동기는 직렬적으로 작동하는 방식이며 비동기는 병렬적으로 작동하는 방식이다. 이 개념을 그대로 가져와 작업 측면에서 보게 되면, 위에서부터 아래로 차례대로 코드를 실행하는 것을 동기라고 부를 수 있다. 이때 순차적으로 진행되므로 작업 흐름을 파악하기 쉽지만, 시간이 상대적으로 오래 걸린다는 단점을 가지고 있다. 반면에 비동기는 순서와 상관없이 독립적으로 작동할 수 있다. 이처럼 비동기는 앞선 작업이 끝날 때까지 기다리지 않아도 된다는 확실한 장점을 가지고 있다. 이번 파트에서는 자바스크립트의 비동기 처리에 대해서 알아볼 것이다.

 

5-(1) setTimeout

 함수 setTimeout은 비동기적으로 동작하는 자바스크립트의 내장 함수이다. setTimeout을 활용해서 time만큼 기다렸다가 출력할 수 있다. turn("3번째", 4000);이 turn("1번째", 2000);보다 위에 있지만 실제로 '1번째 차례'가 먼저 출력된다.

function turn(order, time) { 
    setTimeout(() => {
    console.log(`${order} 차례`);
    }, time);
}

turn("3번째", 4000);
turn("1번째", 2000);
turn("2번째", 3000);

 

5-(2) 콜백 함수로 비동기 처리하기

 콜백 함수의 반환값과 함수 setTimeout의 반환값은 무관하다는 것을 명심해야 한다. 아래 기입한 코드의 경우, 함수 double이 먼저 호출이 된다. 그렇지만 setTimeout이 1초 후로 설정되었고, 이 작업은 비동기 작업이다. 따라서 20이 출력되기를 기다리지 않고 '나이'가 먼저 출력된 후 20이 출력된다. 

function double(num, cb) {
  setTimeout(() => {
    const doubleNum = num * 2;
    cb(doubleNum);
  }, 1000);
}

double(10, (res) => { 
  console.log(res);
});

console.log("나이");

 

5-(3) 프로미스 객체를 이용해 비동기 처리하기

 프로미스(promise)는 특수한 목적을 위해 기능이 여럿 추가된 자바스크립트 내장 객체다. 활용 목적은 비동기 처리이며 콜백 함수를 이용한 것보다 더 쉽게 비동기 작업을 할 수 있다. 관리되는 진행 단계는 다음과 같다

- 대기(Pending): 작업 완료 전

- 성공(Fulfilled): 작업 성공

- 실패(Rejected): 작업 실패

 대기 상태에서 작업이 해결(resolve) 된다면 성공 상태가 된다. 반면에 대기 상태에서 작업을 하지 못하게 되면, 거부(reject) 되어 실패 상태가 된다. 

const promise = new Promise(실행 함수);

 프로미스 객체를 생성할 때 인수는 실행함수(executor)가 된다. 이때 실행 함수는 비동기 작업을 수행하는 함수로 2개(resolve, reject) 매개변수를 제공받게 된다. 프로미스 객체에는 then 메서드를 가지고 있다. then 메서드는 비동기 작업이 아닌 곳에서 결과값을 이용할 때 활용한다.

const promise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    resolve("성공");
  }, 500);
});

promise.then(function (res) { 
  console.log(res);
});
promise.catch(function (err) {
  console.log(err);
});

 

 위와 같은 코드에서 then()은 resolve의 "성공" 값을 콜백 함수의 매개변수 res로 받게 된다. 그렇기 때문에 최종적으로 "성공"이 출력된다. 또한 실패했을 경우 실행할 콜백 함수를 설정하려면 catch 메서드를 활용하면 된다. 이처럼 프로미스의 then과 catch 메서드를 사용하면 작업의 성공실패 여부에 따라 실행할 콜백 함수를 별도로 지정할 수 있게 된다.

 

 

 


출처: 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023).

Corner React.js1
Editor: Ijin

728x90

관련글 더보기