Promise란?

promise는 비동기 함수가 반환하는 객체이다. 함수의 성공 또는 실패 상태를 알려준다. 콜백은 직접 호출하는 방법 대신, Promise로 콜백을 부를 수 있다. 이런 특징 때문에 Promise를 사용하면 비동기 처리 시점, 비동기 함수의 결과를 쉽게 확인할 수 있고 에러도 어디서 일어났는지 파악하기 편리하다.

 

Promise를 생성하는 방법

function requestPayment(paymentData) {
	//...
    return new Promise((resolve, reject) {	//Pending 상태
    	if(isSuccess) {			
        	resolve(data)		// 성공 상태
        } else {
        	reject(error)		// 실패 상태
        }
    })
}

위와 같은 코드가 있다고 했을때 리턴 값으로 Promise를 생성하고 있다. 

 

결제가 요청되기 전에는 Promise가 대기(Pending) 상태이고, 결제 요청에 성공하면, Promise 생성 함수에 있는 resolve() 메서드가 호출된다. Promise가 성공 상태로 바뀌고 data 값을 가지게 된다. 성공 상태를 가진 Promise는 fullfilled된 상태라고 말한다. 하지만 결제 요청에 문제가 있다면 reject()를 호출하여 Promise 상태를 실패로 바꾸고 error 데이터를 가지게 된다. 실패 상태를 가진 Promise는 rejected된 상태라고 말한다. 

 

Promise는 세 개의 상태를 가질 수 있다.

  • 대기(Pending) : 비동기 함수가 아직 시작하지 않은 상태
  • 성공(Fulfilled) : 비동기 함수가 성공적으로 완료된 상태
  • 실패(Rejected) : 비동기 함수가 실패한 상태

 

그렇다면 이렇게 생성된 Promise를 처리하려면 어떻게 해야할까?

 

Promise를 처리할 때는 then() 또는 catch() 메서드를 사용할 수 있다. 각 메서드 파라미터에는 콜백 함수를 넣는데, 결제 요청에 성공 상태의 Promise가 반환되면 then() 메서드가 호출되고, 반대로 실패 상태의 Promise가 반환되면 then() 메서드를 건너뛰고 catch() 메서드가 호출된다.

 

하지만 이러한 Promise에도 단점이 존재한다. then() 체인을 길게 이어 나가면 콜백 체인과 마찬가지로 코드의 가독성이 떨어지고 에러가 어디서 발생했는지 파악하기 어렵다.

 

그래서 async/await을 사용한다. 

 

aync/await이란?

콜백과 Promise 체인의 단점을 async와 await을 사용하여 보완할 수 있다. 

async function handleSubmit() {
	return paymentData
    // return Promise.resolve(paymentData) // 위의 예제와 같은 결과
}

위처럼 함수를 정의할때 async 를 함수 앞에 붙이면, "이 함수는 비동기적인 함수이고 Promise를 반환한다"라고 선언하는 것이다.

반환 값이 Promise 생성 함수가 아니어도 반환되는 값을 Promise 객체에 넣는 것이다.

 

await는 async 함수 안에만 사용할 수 있는 특별한 문법인데, Promise 를 반환하는 함수 앞에 await를 붙이면, 해당 Promise의 상태가 바뀔 때까지 코드가 기다린다. Promise가 성공 상태 또는 실패 상태로 바뀌기 전까지는 다음 연산을 시작하지 않는 것이다.

 

async function requestPayment(paymentData) {
	//...
    const paymentData = await payment.requestPayment({});
    
    console.log(paymentData);
    return paymentData
}

위의 예시를 보면 requestPayment() 메서드 앞에 await 이 붙어 있는 것을 알 수 있다. 결제 요청이 완료되어 Promise객체의 상태가 바뀐 이후에만 console.log(paymentData) 가 실행된다. 비동기 작업을 동기로 바꾸는 것이다.

 

사실상 await은 then()과 같은 역할을 하는데, 콜백 함수를 등록할 필요가 없기 때문에 더 편리하다. 또 체이닝으로 인해 코드가 복잡해질 필요도 없다.

 

그렇다면 에러 처리는 어떻게 할까? async/await 로 코드를 동기적으로 바꾸는 것으로 생각할 수 있다고 했는데, try/catch를 사용하면 된다. 

async function handleSubmit() {
      try {
        const paymentData = await paymentWidget.requestPayment({
          orderId: "EKRUWOIIWO10-2931", // 주문 ID(직접 만들어주세요)
          orderName: "t-shirt" // 주문명
        });
		console.log(paymentData);
        // 성공 처리
        return paymentData;
      } catch (error) {
        // 에러 처리
        console.log(error.message);
      }
    }

위 코드에서 requestPayment() 메서드를 실패하여 실패 상태의 Promise객체를 반환하면, 바로 catch 블록을 실행한다.

'Front-End > React&Next.js' 카테고리의 다른 글

React 기본  (0) 2024.03.14

1. Component

 - Reac의 가장 큰 특징이자 장점은 Component-based, 컴포넌트 기반이라는 것이다. 컴포넌트란 하나의 의미를 가진 독립적인 단위의 모듈로 나만의 HTML이라고 생각할 수 있다. UI의 일부를 제어하는 재사용 가능한 코드이다. 또 JavaScript에서 함수의 역할을 React에게서 담당하는 것과 같다. 

  • 함수형 컴포넌트
  • 클래스 컴포넌트

  1-1 함수형 컴포넌트

function Welcome(props) {
	return <h1> Hello, {props.name} </h1>;
}

 위 예시에서 Welcome은 함수형 컴포넌트로, props를 인수로 받아 React 요소를 반환한다.

 

 1-2 클래스 컴포넌트

class Welcome extends React.Component {
	render() {
    	return <h1>Hello, {this.props.name}</h1>;
    }
}

 

클래스 컴포넌트의 경우 더 복잡하고 더 많은 기능을 제공한다. render 메서드를 포함해야 한다.

 

*함수형 컴포넌트에서 React Hooks 사용

  React Hooks는 함수 컴포넌트에서 상태와 다른 React 기능을 사용할 수 있도록 하는 함수이다. 클래스 컴포넌트로 변환하지 않고도 함수 컴포넌트에서 사용가능하다.

 

 * useState와 useEffect Hooks 이해

  useState와 useEffect는 React Hooks를 사용하는데 기본적인 역할을 한다.

  • useState : 컴포넌트에 상태를 추가하는 데 사용한다. 초기 상태를 인수로 받고 현재 상태와 상태를 업데이트하는 함수로 구성된 배열을 반환한다.
const [count, setCount] = useState(0);

// 해당 예시에서 const는 현재 상태이고 setCount는 상태를 업데이트하는 함수이다.
  • useEffect : 이 훅은 클래스 컴포넌트의 componentDidMount, componentDidUpdate, componentWillUnmount 라이프사이클 메서드와 같은 역할을 한다. 기본적으로 첫 번째 렌더링을 포함하여 모든 렌더링 이후에 실행된다.
useEffect(() => {
	document.title = `You clicked ${count} times`;
}, [count]);

// 이 예시에서 useEffect 혹은 count 상태가 변경될 때마다 문서 제목을 업데이트한다.

 

 

 

2. Props

props는 상위 컴포넌트가 하위 컴포넌트에게 내려주는 데이터이다. props는 객체이다. props는 읽기 전용이며, 받은 컴포넌트에서 변경해서는 안된다.

function Greeting(props) {
	return <h1>Hello, {props.name}</h1>;
}

위 예제에서 Alice 문자열을 Greeting 컴포넌트에 props로 전달한다.컴포넌트 내부에서는 이 props를 props.name으로 접근할 수 있다. 이러한 Props는 왜 사용하는 것일까?

  1. 데이터 흐름 : React에서 데이터 흐름은 단방향적이다. 즉, 데이터는 부모 컴포넌트에서 자식 컴포넌트로 흐르는 구조이다. 이로써 데이터 흐름이 예측 가능하고 추적하기 쉬워지며, 디버깅이나 테스트 시에 유용하다.
  2. 컴포넌트 재사용성: props를 사용하면 재사용 가능한 컴포넌트를 만들 수 있다. 컴포넌트에 값을 하드코딩하는 대신, props를 사용하여 동일한 컴포넌트에 다른 데이터를 전달함으로써 컴포넌트를 동적이고 재사용 가능하게 만들 수 있다.
  3. 관심사의 분리 : props를 사용하면 컴포넌트가 더 독립적이고 캡슐화된 형태가 된다. 컴포넌트는 앱의  다른 상태를 알 필요가 없으며, 자신의 데이터를 표시하고 자체 상태를 처리하는 데 집중할 수 있다.
  4. 컴포넌트간의 통신 : props를 통해 데이터뿐만 아니라 함수도 자식 컴포넌트로 전달할 수 있다. 이는 자식 컴포넌트가 props로 전달받은 함수를 호출하여 부모 컴포넌트에게 통신할 수 있는 것을 의미한다.

* PropTypes를 사용한 프롭스 유효성 검사

import PropTypes from 'prop-types';

function Greeting(props) {
	return <h1>Hello, {props.name}</h1>;
}

Greeting.propTypes = {
	name: PropTypes.string.isRequired,
};

PropTypes는 JavaScript 객체의 유형을 검사하는 데 사용되는 라이브러리이다. 해당 라이브러리를 사용하여 컴포넌트가 받는 프롭스가 올바른 유형인지 확인할 수 있다. 

 

 

3. State

state는 컴포넌트가 독립적으로 갖는 상태이다. 이 역시 객체의 형태로, 컴포넌트 안에서만 제어되어 보관, 관리된다.

welcome 메세지 뒤에 메세지가 렌더링 될 때의 현재 시각을 알려주는 메세지를 같이 렌더링 해보자.

class CLock extends React.Component {
	constructor(props) {
    	super(props)l
        this.state = {date : new Date()};
    }
    
render() {
	return (
    	<div>
        	<h1>Hello, {this.props.name}</h1>
            <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
        </div>
    );
 }
}

ReactDOM.render(
	<Clock name="Jaon"/>,
    document.getElementById('root')
);


// Clock 이라는 컴포넌트를 만들고 date라는 state를 지정해주면 

// Hello, world!
// it is 오후 4:16:26.

// 이렇게 출력이 된다.

'Front-End > React&Next.js' 카테고리의 다른 글

Promise / async / await  (1) 2024.04.03

+ Recent posts