connecting dots

Recap | Beginner Live Class Recap 2,3회차 본문

Live Class/Beginner

Recap | Beginner Live Class Recap 2,3회차

dearsuhyun 2024. 8. 22. 13:53

2-1. 리액트에서는 단일요소가 return 되어야 한다

 

'단일 요소여야 한다'

=

return 문에서 JSX를 반환할 때, 반드시 하나의 최상위 요소로 감싸야 한다

 

리액트 컴포넌트는 JSX를 반환할 때, 여러 요소를 반환할 수 있지만, 이 요소들을 하나의 부모 요소로 감싸야 한다.

그렇지 않으면 위와 같은 에러가 발생한다.

 

해결 방법

1) 단일 부모 요소로 감싸기

function MyComponent() {
  return (
    <div>
      <h1>Hello</h1>
      <p>This is a paragraph.</p>
    </div>
  );
}

 

2) React Fragement 사용

부모 요소가 필요 없지만 여전히 다수의 요소를 반환해야 한다면, React.Fragment 또는 빈 태그(<>...</>)를 사용할 수 있다.

function MyComponent() {
  return (
    <>
      <h1>Hello</h1>
      <p>This is a paragraph.</p>
    </>
  );
}

 

2-2. 구조분해할당 이해하기

구조 분해 할당(Destructuring Assignment)

자바스크립트에서 배열이나 객체의 값을 간편하게 변수에 할당하는 방법

 

1) 배열 구조분해할당

[b, c, d] = numbersnumbers 배열의 첫 번째, 두 번째, 세 번째 값을 각각 b, c, d에 할당

--> 인덱스를 일일이 지정할 필요 없이, 배열의 값을 간편하게 꺼내어 사용할 수 있음

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

// 기존 방식
const b = numbers[0];
const c = numbers[1];
const d = numbers[2];

// 구조 분해 할당 방식
const [b, c, d] = numbers;

console.log(b); // 1
console.log(c); // 2
console.log(d); // 3

 

2) 객체 구조분해할당

{ name } = usersusers 객체에서 name 속성의 값을 변수 name에 할당

--> 객체에서 원하는 속성을 간편하게 꺼내어 사용할 수 있음

const users = {
  name: "Mike",
  age: 30
};

// 기존 방식
// const name = users.name;

// 구조 분해 할당 방식
const { name } = users;

console.log(name); // "Mike"

 

3) 구조분해할당 기능

- 기본값 할당 지정

이 코드는 users 객체에서 email 속성을 찾는다.

만약 users 객체에 email 속성이 없거나 undefined일 경우, "suhyun8667@naver.com"이라는 기본값이 대신 사용된다.

이 기본값은 email이라는 속성이 없거나 정의되지 않았을 때 안전하게 사용할 값을 미리 지정하는 데 유용하다.

만약 users 객체에 email 속성이 있다면, 그 값이 email 변수에 할당된다.

const { email = "suhyun8667@naver.com" } = users;
console.log(email);

 

• 만약 users = {}라면:
email 변수는 "suhyun8667@naver.com" 값을 가지고

• 만약 users = { email: "anotheremail@example.com" }라면:
email 변수는 "anotheremail@example.com" 값을 가진다

 

- 변수 이름 변경

이 코드는 users 객체에서 age 속성을 찾아서 그 값을 userAge라는 새로운 변수에 할당한다.

즉, age라는 속성을 userAge라는 이름으로 바꾸어서 사용할 수 있게 해주는 것 !

변수 이름이 이미 다른 부분에서 사용되고 있거나, 더 의미 있는 이름으로 바꿔서 사용하고 싶을 때 유용하다.

const { age: userAge } = users;
console.log(userAge);

 

3-1. Typescript에서 type과 interface의 차이점

1. 기본적인 차이점

 

interface: 주로 객체의 구조를 정의하는 데 사용됩니다. 객체가 가져야 할 속성의 이름과 타입을 명확히 정의합니다.

type: 더 일반적인 타입을 정의할 때 사용됩니다. 객체뿐만 아니라 기본 타입(예: 문자열, 숫자)이나 유니언 타입, 튜플 등을 정의할 수 있습니다.

 

2. 코드 분석

 

export interface ICountValue {
  a: number;
  b: string;
}

ICountValue라는 인터페이스를 정의하여, a라는 숫자 속성과 b라는 문자열 속성을 가진 객체 타입을 만들었다

이 인터페이스는 주로 객체의 구조를 정의할 때 사용되며, 객체의 필수 속성과 타입을 명확하게 지정한다.

 

export type CountValue = number;

CountValue라는 타입을 정의하여, 숫자 타입의 별칭을 만들었다. 즉, CountValuenumber와 같은 의미 !

type은 인터페이스처럼 객체의 구조뿐만 아니라, 기본 타입(number, string 등), 유니언 타입, 교차 타입 등을 정의할 수 있다

 

3. 구체적인 차이점

 

확장성

인터페이스확장이 가능하다. 즉, 다른 인터페이스를 상속받아 확장 가능 !

interface IBase {
  a: number;
}

interface IExtended extends IBase {
  b: string;
}

 

타입은 교차 타입(Intersection Types)을 통해 확장 가능

type Base = {
  a: number;
};

type Extended = Base & {
  b: string;
};

 

유니언 타입 정의

 

type을 사용하면 유니언 타입을 정의할 수 있습니다. 인터페이스는 유니언 타입 직접적으로 표현하지 못함

type StringOrNumber = string | number;
// string or number

 

중복 선언 가능성

 

인터페이스는 같은 이름으로 여러 번 선언할 수 있으며, 선언된 내용은 자동으로 병합된다.

--> “선언 병합(Declaration Merging)”

타입은 같은 이름으로 중복 선언할 수 없으며, 중복 선언 시 오류가 발생한다.

interface IUser {
  name: string;
}

interface IUser {
  age: number;
}

const user: IUser = {
  name: "Alice",
  age: 30,
};

 

4. 언제 무엇을 사용할까?

 

interface: 객체의 구조를 정의하고, 이 구조를 상속받거나 확장할 필요가 있는 경우 인터페이스를 사용하는 것이 좋습니다. 특히 클래스 기반의 구조에서 사용하기 적합합니다.

type: 기본 타입의 별칭을 만들거나, 유니언 타입, 교차 타입, 튜플 등을 정의할 때 사용합니다. 복잡한 타입 조합이 필요한 경우 유용합니다.

 

 

3-2. 비동기적으로 데이터 요청하기

async function fetchData() {
  const responseObject = await fetch('https://api.heropy.dev/v0/users');
  const data = await responseObject.json(); 

  console.log(data);
}

fetchData(); // 함수 호출

 

1. fetchData 함수가 호출된다.

async 키워드로 정의된 이 함수는 내부에서 await 키워드를 사용하여 비동기 작업을 처리한다.

비동기 함수는 기본적으로 Promise를 반환한다.

 

2. API 요청을 보낸다 (fetch).

fetch('https://api.heropy.dev/v0/users')는 API로 HTTP 요청을 보내는 비동기 작업이다. 이 작업은 사용자 데이터를 가져오기 위한 요청을 수행한다.

이 요청은 비동기적으로 수행되며, 네트워크 요청이 완료될 때까지 코드가 멈추지 않고 다른 작업을 수행할 수 있다.

 

3. await로 응답을 기다린다.

await 키워드를 사용하면, fetch 요청이 완료될 때까지 코드 실행이 일시 정지된다.

fetch가 완료되면 서버에서 응답이 도착하고, 이 응답은 responseObject 변수에 저장된다.

 

4. JSON 데이터로 변환한다 (responseObject.json()).

responseObject.json()은 응답 본문을 JSON 형태로 변환하는 또 다른 비동기 작업이다.

await 키워드를 사용하여 JSON 변환이 완료될 때까지 기다리고, 변환된 데이터는 data 변수에 저장된다.

 

5. 콘솔에 데이터를 출력한다.

변환된 JSON 데이터가 data 변수에 저장된 후, 이 데이터를 console.log(data)를 통해 콘솔에 출력한다.

 

6. 함수를 호출하여 실행한다.

마지막으로 fetchData();를 호출하여 이 비동기 함수가 실행된다.

 

3-3. 화면에 user 목록 띄우기 예제

 

const [users, setUsers] = useState([])
  // 여기에서 빈 배열에는 무엇이 들어가 있을지 알 수가 없음
  // 즉 이 배열데이터가 어떤 타입인지 모르는 상태
  // ts는 쌩으로 빈 배열이라고 판단하고 이 안에는 아무것도 들어갈 수 없다고 판단함
  // never[]
  // 이렇게만 해주면 밑에 jsx 문법 부분에서 {user.name}에 오류 발생
  // user는 never이기 때문에 name이라는 속성이 존재 하지 않아서 오류가 발생함
  
  const [users, setUsers] = useState<User[]>([])
  // 따라서 Users의 배열이라고 type을 직접 명시해줘야 함

 

const [count, setCount] = useState(31)
  // 이 부분의 경우는 초기 값이 이미 number이기 때문에 type 추론 가능, 따로 명시 안해줘도 됨

 

3-4. 함수 호이스팅

함수 호이스팅은 함수 선언식일 때만 가능 !

// 함수 표현식 (할당 연산자가 있는 것)
// 호이스팅 되지 않음.
const getUsers = async () => {}

// 함수 선언식
// 호이스팅 가능. 위로 올라가서 정의되기 때문에 정상적으로 동작
async function getUsers() {}

 

3-5. promise 객체

promise 객체란 ?

자바스크립트 Promise 객체는 비동기 작업의 최종 완료 또는 실패를 나타내는 Array나 Object 처럼 독자적인 객체라고 보면 된다. 

비동기 작업이 끝날 때까지 결과를 기다리는 것이 아니라, 결과를 제공하겠다는 '약속'을 반환한다는 의미에서 Promise라 지어졌다고 한다. 

 

promise 객체 생성

Promise 객체를 생성하려면 new 키워드와 Promise 생성자 함수를 사용하면 된다. 

이때 Promise 생성자 안에 두개의 매개변수를 가진 콜백 함수를 넣게 되는데, 첫 번째 인수는 작업이 성공했을 때 성공(resolve)임을 알려주는 객체이며, 두 번째 인수는 작업이 실패했을 때 실패(reject)임을 알려주는 오류 객체이다.

const myPromise = new Promise((resolve, reject) => {
	// 비동기 작업 수행
    const data = fetch('서버로부터 요청할 URL');
    
    if(data)
    	resolve(data); // 만일 요청이 성공하여 데이터가 있다면
    else
    	reject("Error"); // 만일 요청이 실패하여 데이터가 없다면
})

 

 

propmise 객체의 3가지 상태

출처 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

 

1) pending

이행하지도, 거부하지도 않은 초기 상태

작업이 진행 중임을 의미

 

2) resolved

프로미스의 상태가 확정되었음을 의미

이 확정된 상태는 fulfilled(성공)일 수도 있고, rejected(실패)일 수도 있음

 

3-1) fulfilled

작업이 성공적으로 완료됨을 의미

3-2) rejected

작업이 실패했음을 의미

 

 

promise의 메서드

1) .then()

 promise 객체가 fulfilled 상태가 되면 실행할 콜백함수를 등록하는 메서드

 

2) .catch()

 promise 객체가 rejected 상태가 되면 실행할 콜백함수를 등록하는 메서드

 

3) .finally()

어떤 작업의 성공, 실패 여부와 상관없이 항상 실행하고 싶은 콜백함수를 등록하는 메서드

myPromise
    .then((value) => { // 성공적으로 수행했을 때 실행될 코드
    	console.log("Data: ", value); // 위에서 return resolve(data)의 data값이 출력된다
    })
    .catch((error) => { // 실패했을 때 실행될 코드
     	console.error(error); // 위에서 return reject("Error")의 "Error"가 출력된다
    })
    .finally(() => { // 성공하든 실패하든 무조건 실행될 코드
    	
    })

 

 

프로미스 동시성(Promise concurrency)를 지원하는 메소드

  • Promise.all()
  • Promise.allSettled()
  • Promise.any()
  • Promise.race()

1) Promise.all()

Promise.all()은 주어진 모든 프로미스가 모두 이행(fulfilled)될 때까지 기다립니다.

모든 프로미스가 성공적으로 완료되면 결과들을 배열로 반환합니다. 만약 하나라도 거부(rejected)되면, 전체가 거부 상태로 바뀝니다.

 

사용 예: 여러 비동기 작업(예: 여러 개의 API 호출)이 모두 성공해야 할 때.

let promise1 = Promise.resolve('작업 1 완료');
let promise2 = Promise.resolve('작업 2 완료');
let promise3 = Promise.resolve('작업 3 완료');

Promise.all([promise1, promise2, promise3]).then((results) => {
  console.log(results); // ['작업 1 완료', '작업 2 완료', '작업 3 완료']
});

 

2) Promise.allSettled()

Promise.allSettled()은 주어진 모든 프로미스가 완료(settled)될 때까지 기다립니다. 이때 완료된 프로미스는 성공이든 실패든 상관없이 결과를 반환합니다. 결과는 각 프로미스의 상태(fulfilled 또는 rejected)와 함께 배열로 반환됩니다.

 

사용 예: 성공과 실패 여부에 관계없이 모든 비동기 작업이 끝난 후 결과를 처리하고자 할 때.

let promise1 = Promise.resolve('작업 1 완료');
let promise2 = Promise.reject('작업 2 실패');
let promise3 = Promise.resolve('작업 3 완료');

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
  console.log(results);
  // [{status: 'fulfilled', value: '작업 1 완료'},
  //  {status: 'rejected', reason: '작업 2 실패'},
  //  {status: 'fulfilled', value: '작업 3 완료'}]
});

 

3) Promise.any()

Promise.any()는 주어진 프로미스 중 가장 먼저 성공한(fulfilled) 프로미스의 결과를 반환합니다.

모든 프로미스가 거부(rejected)되면 AggregateError가 발생합니다.

 

사용 예: 여러 비동기 작업 중에서 하나라도 성공하면 그 결과를 사용하고, 모두 실패했을 때만 에러를 처리하고자 할 때.

let promise1 = new Promise((resolve, reject) => setTimeout(reject, 100, '실패'));
let promise2 = new Promise((resolve) => setTimeout(resolve, 200, '성공!'));
let promise3 = new Promise((resolve) => setTimeout(resolve, 300, '또 다른 성공!'));

Promise.any([promise1, promise2, promise3]).then((result) => {
  console.log(result); // '성공!'이 출력됩니다.
});

 

4) Promise.race()

Promise.race()는 주어진 프로미스 중 가장 먼저 완료된 프로미스의 결과를 반환합니다.

첫 번째로 완료된 프로미스가 성공이든 실패든 관계없이 그 결과가 반환됩니다.

 

사용 예: 여러 비동기 작업 중에서 가장 빨리 완료된 작업의 결과만 필요할 때.

let promise1 = new Promise((resolve) => setTimeout(resolve, 500, '500ms'));
let promise2 = new Promise((resolve) => setTimeout(resolve, 100, '100ms'));

Promise.race([promise1, promise2]).then((result) => {
  console.log(result); // '100ms'가 출력됩니다.
});

 

cf. 이터러블(Iterable)
자바스크립트에서 반복 가능한 객체(여러 개의 값을 하나씩 순서대로 접근할 수 있는 객체)를 의미

예시
1. 배열(Array):
예를 들어, 사과, 바나나, 오렌지가 들어 있는 과일 바구니가 있다고 해보세요. 이 바구니에서 하나씩 과일을 꺼낼 수 있다면, 이 바구니는 이터러블입니다. 자바스크립트에서 배열은 대표적인 이터러블입니다. [사과, 바나나, 오렌지]처럼 배열에 여러 개의 요소가 들어 있고, 이걸 하나씩 꺼내볼 수 있는 거죠.

2. 문자열(String):
“hello”라는 단어를 생각해봅시다. 이 단어에서 ‘h’, ‘e’, ‘l’, ‘l’, ‘o’를 하나씩 꺼낼 수 있다면, 이 문자열도 이터러블입니다.
자바스크립트에서 문자열도 하나씩 문자를 꺼낼 수 있기 때문에 이터러블입니다.


Promise.any()와 연결하기
Promise.any()는 여러 개의 프로미스(Promise) 중에서 가장 먼저 성공한(이행된) 결과를 얻고 싶을 때 사용합니다. 이때 여러 개의 프로미스를 배열 같은 이터러블에 넣어서 Promise.any()에 전달할 수 있습니다.
let promise1 = new Promise((resolve, reject) => setTimeout(reject, 100, '실패'));
let promise2 = new Promise((resolve) => setTimeout(resolve, 200, '성공!'));
let promise3 = new Promise((resolve) => setTimeout(resolve, 300, '또 다른 성공!'));

Promise.any([promise1, promise2, promise3]).then((result) => {
  console.log(result); // '성공!'이 출력됩니다.
});​

여기서 [promise1, promise2, promise3]는 이터러블(하나씩 꺼내볼 수 있는 배열)입니다. Promise.any()는 이 배열에서 가장 먼저 성공한 promise2의 결과인 '성공!'을 반환합니다.

 

 

Promise() 생성자

new Promise(executor);

 

Promise 객체는 new 키워드와 생성자를 사용해 만듭니다.

생성자는 매개변수로 "실행 함수"를 받습니다.

이 함수는 매개 변수로 두 가지 함수를 받아야 하는데, 첫 번째 함수(resolve)는 비동기 작업을 성공적으로 완료해 결과를 값으로 반환할 때 호출해야 하고, 두 번째 함수(reject)는 작업이 실패하여 오류의 원인을 반환할 때 호출하면 됩니다.

두 번째 함수는 주로 오류 객체를 받습니다.

let promise = new Promise(function(resolve, reject) {
  // 비동기 작업을 실행하는 코드가 여기에 들어갑니다.

  if (/* 작업이 성공하면 */) {
    resolve('성공 결과'); // 성공 시 호출
  } else {
    reject('실패 이유'); // 실패 시 호출
  }
});

 

cf. promise instance

new Promise()를 호출하여 생성된 개별적인 프로미스 객체

(위 예제에서 promise라는 변수에 할당된 값을 프로미스 인스턴스라고 함)

인스턴스는 특정 비동기 작업을 추적하며, 그 작업이 성공했는지, 실패했는지, 또는 아직 진행 중인지 상태를 관리합니다.

 

매개변수:

- executor 함수: new Promise()는 두 개의 매개변수, resolvereject를 받는 콜백 함수를 인자로 받습니다. 이 콜백 함수는 프로미스가 생성될 때 자동으로 실행됩니다.

- resolve: 비동기 작업이 성공했을 때 호출합니다. 이때 인자로 전달된 값은 프로미스의 결과로 사용됩니다.

- reject: 비동기 작업이 실패했을 때 호출합니다. 이때 인자로 전달된 값은 에러 정보로 사용됩니다.

 

예제

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("1초 후에 작업이 완료되었습니다.");
  }, 1000);
});

promise.then((result) => {
  console.log(result); // "1초 후에 작업이 완료되었습니다." 출력
}).catch((error) => {
  console.error(error); // 만약 reject()가 호출되었다면 이 블록이 실행됩니다.
});

 

 

 

 


https://www.freecodecamp.org/korean/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/

 

자바스크립트 프로미스 튜토리얼 - 자바스크립트의 프로미스를 이행하거나 거부하는 방법

프로미스는 자바스크립트에서 비동기 처리를 위한 중요한 요소이지만 프로미스를 이해하고 적용하는 것은 그렇게 쉽지 않다고 생각하셨을 수도 있습니다. 하지만 절 따라오세요, 여러분은 혼

www.freecodecamp.org

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise#%EC%BD%9C%EB%B0%B1_%ED%95%A8%EC%88%98%EC%9D%98_%EB%B9%84%EB%8F%99%EA%B8%B0_%EC%B2%98%EB%A6%AC_%EB%AC%B8%EB%B2%95

 

📚 자바스크립트 Promise 개념 & 문법 정복하기

콜백 지옥을 탈출하는 새로운 문법 자바스크립트에서 '비동기 처리' 란 현재 실행중인 작업과는 별도로 다른 작업을 수행하는 것을 말한다. 예를 들어 서버에서 데이터를 받아오는 작업은 시간

inpa.tistory.com

 

반응형