2025 / 02 / 17

오늘 수업 시간에는 동기식 / 비동기식 프로그래밍에 대해 배웠습니다.
항상 헷갈렸었는데, 다른 사람에게 설명하면서 공부하니 조금 더 괜찮았습니다.
그 다음에는 Promise 객체에 대해 배웠습니다. 정보처리산업기사에서 개념으로만 접했던 내용을 실제로 사용해보고 이해하는 시간을 가졌습니다.



💌 동기식 프로그래밍

Synchronous Programming

  • 동기식 프로그래밍은 순차적으로 작업을 처리하는 방식입니다.
  • 한 작업이 끝난 후에 다음 작업을 실행합니다.
  • 첫 번째 작업이 완료되기 전까지는 두 번째 작업이 실행되지 않습니다.
function synxWork() {
  const delayUntil = new Date().getTime();  // 현재 시간을 밀리초로 반환
  const twoSecondLater = delayUntil + 2000; // 2초 뒤의 시간 계산

  // 현재 시간이 2초가 지날 때까지 기다림
  while (Date.now() < twoSecondLater);  
  console.log("완료");
}

console.log("hello");
synxWork(); // 2초 동안 기다림
console.log("world");
  • 동기 방식에서는 코드가 위에서 아래로 차례차례 실행됩니다.
  • synxWork( ) 함수는 2초 동안 기다리는 작업을 합니다.
  • hello가 먼저 출력되고, 그 다음에 2초 후에 완료가 출력됩니다.
  • 그 후에 world가 출력됩니다.


💌 비동기식 프로그래밍

Asynchronous Programming

  • 비동기식 프로그래밍은 작업을 동시에 진행할 수 있는 방식입니다.
  • 어떤 작업이 끝나지 않아도 다른 작업을 실행할 수 있게 해줍니다.
  • 주로 시간이 오래 걸리는 작업을 처리할 때 유용합니다.
function asyncWork() {
  setTimeout(() => {
    console.log("완료");
  }, 2000);  // 2초 후에 '완료' 출력

  console.log("작업시작");
}

asyncWork(); // '작업시작'이 먼저 출력되고, 2초 후에 '완료'가 출력됩니다.
  • 작업이 완료되기를 기다리지 않고, 다른 코드가 실행되는 동안 작업을 처리합니다.
  • setTimeout( ) 함수는 2초 후에 완료를 출력하지만, 코드가 실행될 때 작업시작이 먼저 출력됩니다.


💌 콜백 헬

Callback Hell

  • 콜백 함수는 특정 작업이 완료된 후 호출되는 함수입니다.(비동기 방식에서 주로 사용)
  • 작업을 완료하는 시점을 알리거나 그 후에 다른 작업을 실행할 때 유용합니다.
  • 중첩된 콜백 함수를 사용하면 코드가 깊이 중첩되어 관리하기 어려워집니다.
  • 콜백 함수가 중첩된 이러한 현상을 콜백 헬이라고 합니다.
function callback() {
  setTimeout(() => {
    console.log("작업 완료1");

    setTimeout(() => {
      console.log("작업 완료2");

      setTimeout(() => {
        console.log("작업 완료3");

        setTimeout(() => {
          console.log("작업 완료4");
        }, 2000);
      }, 2000);
    }, 2000);
  }, 2000);
}

callback();


💌 Promise 객체

  • 프로미스는 비동기 작업의 완료 또는 실패를 다루는 객체입니다.
  • 비동기 작업이 완료되었을 때 성공 / 실패를 처리하는 방식입니다.
  • 콜백 함수 대신 사용합니다.
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("성공");  // 2초 후에 성공 메시지 전달
  }, 2000);
});

promise.then((message) => {
  console.log(message);  // '성공'이 출력됨
});
  • 프로미스 객체는 비동기 작업이 성공하거나 실패했을 때 상태를 처리합니다.
  • resolve( )는 성공적인 결과를 전달하고, reject( )는 오류를 전달합니다.
  • then( )은 작업이 성공했을 때 실행되며, catch( )는 실패했을 때 실행됩니다.


1) 순차적인 작업

  • 프로미스를 사용하면 여러 개의 비동기 작업을 순차적으로 실행할 수 있습니다.
  • 아래의 예시는 1초, 2초, 3초 후에 순차적으로 메시지를 출력하는 예시입니다.
const getPromise1 = (second) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`프로미스 1 ${second}`);
    }, second * 1000);
  });
};

const getPromise2 = (second) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`프로미스 2 ${second}`);
    }, second * 1000);
  });
};

const getPromise3 = (second) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`프로미스 3 ${second}`);
    }, second * 1000);
  });
};

getPromise1(1)
  .then((message) => {
    console.log(message);  // 1초 후에 '프로미스 1 1' 출력
    return getPromise2(2);
  })
  .then((message) => {
    console.log(message);  // 2초 후에 '프로미스 2 2' 출력
    return getPromise3(3);
  })
  .then((message) => {
    console.log(message);  // 3초 후에 '프로미스 3 3' 출력
  });


2) 프로미스 체이닝

  • then( ) 메서드는 반환된 프로미스를 이어서 실행할 수 있게 해줍니다.
  • 콜백 헬을 피하고 코드의 가독성을 높이는 데 유용합니다.


3) 오류 처리 & 클린업

  • catch( ) 메서드를 사용하여 오류를 처리할 수 있습니다.
  • finally( )는 비동기 작업이 성공하든 실패하든 항상 실행되는 메서드입니다.
  • 주로 클린업 작업이나 마지막 처리를 할 때 사용됩니다.
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("error");  // 2초 후에 오류 발생
  }, 2000);
});

promise2
  .then((message) => {
    console.log("성공", message);
  })
  .catch((error) => {
    console.log("실패", error);  // '실패 error' 출력
  })
  .finally(() => {
    console.log("마지막");  // 성공/실패 여부 관계없이 항상 실행
  });


💌 Promise 정리

  • 프로미스를 간단하게 표로 정리해보았습니다!
용어설명사용 예시
Promise          비동기 작업의 완료 또는 실패를 처리하는 객체입니다. 작업이 완료되었을 때 resolve 또는 reject를 통해 상태를 변경합니다.let promise = new Promise((resolve, reject) => { resolve("성공"); });
resolve        성공적인 결과를 전달합니다.resolve("작업 완료!");
reject        오류 메시지를 전달합니다.reject("오류 발생!");
then( )        resolve가 호출된 후 실행되는 메서드입니다. 비동기 작업이 성공적으로 완료되었을 때 그 결과를 처리합니다.promise.then(result => { console.log(result); });
catch( )        reject가 호출된 후 실행되는 메서드입니다. 비동기 작업이 실패했을 때 오류를 처리합니다.promise.catch(error => { console.log(error); });
finally( )        비동기 작업이 성공하거나 실패하든 관계없이 항상 실행되는 메서드입니다.promise.finally(() => { console.log("마지막 처리"); });
체이닝        여러 개의 then( )을 이어서 사용할 수 있어, 비동기 작업을 순차적으로 처리할 수 있습니다.promise.then(result => { return nextPromise; }).then(result => { ... });

  • 따로 작성해서 정리한 간단한 예시 입니다.
// 프로미스 예시
const examplePromise = new Promise((resolve, reject) => {
  const isSuccess = true; // 성공 여부
  if (isSuccess) {
    resolve("작업 성공!");
  } else {
    reject("작업 실패!");
  }
});

examplePromise
  .then((message) => {
    console.log(message);  // "작업 성공!" 출력
  })
  .catch((error) => {
    console.log(error);    // "작업 실패!" 출력
  })
  .finally(() => {
    console.log("마지막 처리 완료");  // 항상 실행
  });


💌 fetch( )

  • HTTP 요청을 보내고 응답을 처리할 수 있는 javascript의 내장 함수입니다.
  • 이 함수는 url을 입력받아 요청을 보냅니다.
  • 그 후 서버에서 돌아온 응답을 비동기적으로 처리할 수 있게 해줍니다.

1) 기본 사용법

  • fetch( )는 Promise 객체를 반환합니다.
  • 네트워크 요청이 성공적으로 완료되면 resolve 상태로 데이터를 반환하고, 실패하면 reject 상태로 에러를 반환합니다.
fetch(url)
  .then(response => {
    // 응답이 성공적으로 돌아왔을 때 실행되는 코드
  })
  .catch(error => {
    // 에러가 발생했을 때 실행되는 코드
  });


2) 데이터 요청 & 응답 받기

  • 가장 기본적인 fetch( )의 예시는 서버에서 데이터를 받아오는 것입니다.
  • then( )catch( ) 메서드를 사용하여 응답을 처리할 수 있습니다.
fetch("https://randomuser.me/api/")
  .then((response) => {
    return response.json();  // 응답을 JSON 형식으로 변환
  })
  .then((data) => {
    console.log(data);        // 받아온 데이터 출력
    console.log(data.results[0].gender);  // 첫 번째 사용자 성별 출력
  })
  .catch((error) => {
    console.error("Error:", error);  // 에러 처리
  });

fetch("https://randomuser.me/api/")

  • fetch( ) 함수는 API 주소를 인자로 받아 네트워크 요청을 보냅니다.
  • https://randomuser.me/api/라는 URL에서 랜덤 사용자 데이터를 요청하고 있습니다.

then((response) => response.json())

  • 서버에서 응답을 받으면, 그 응답은 Response 객체로 전달됩니다.
  • 객체를 .json( ) 메서드를 통해 JSON 형식으로 변환합니다.
  • 이 메서드는 비동기적으로 동작하므로, 다른 then( ) 체이닝을 사용하여 데이터를 처리할 수 있습니다.

then((data) => {...})

  • JSON으로 변환된 데이터를 여기서 받아와 처리합니다.
  • data.results[0].gender와 같은 경로를 통해 원하는 값을 추출할 수 있습니다.

catch((error) => {...})

  • 요청이 실패했거나, 네트워크 오류가 발생했을 때 실행됩니다.
  • catch( ) 메서드는 에러 메시지를 출력하거나, 오류를 처리할 수 있도록 도와줍니다.


3) 사용 시 주의사항

1) CORS

  • 다른 도메인에서 데이터를 요청할 경우 CORS 정책에 의해 요청이 차단될 수 있습니다.
  • 서버 측에서 CORS 헤더를 설정해주어야 합니다.

2) 에러 처리

  • 네트워크 오류나 잘못된 응답이 올 수 있으므로 catch()를 사용하여 반드시 오류를 처리해야 합니다.

3) 응답 상태 코드

  • fetch( )는 요청이 성공했는지, 실패했는지에 대한 정보를 제공하지 않습니다.
  • 서버가 404 오류를 반환해도 fetch()는 정상적으로 처리된 것처럼 보일 수 있습니다.
  • 이 경우 응답 객체의 response.ok 속성을 확인해 상태를 처리할 수 있습니다.
fetch("https://example.com/api")
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error("error", error));
  • response.ok가 true라면 응답은 성공적인 것입니다.
  • response.ok가 false라면, 요청이 실패했거나 서버가 오류를 반환한 것입니다. (예: 404, 500 상태 코드 등).


4) 전체적인 순서

fetch( ) 전체적인 순서
1) fetch( )로 API에 요청을 보냅니다.
2) 응답이 성공적이었는지 확인합니다.
3) 성공적이면 데이터를 JSON 형식으로 변환해줍니다.
4) 실패하면 오류 메시지를 던지고, catch( )에서 그 오류를 처리하게됩니다.




29일차 후기

  • 비동기식과 동기식에 대한 내용은 산업기사 준비할 때 들어봐서 아는 내용이었습니다.
  • 하지만 프로미스 객체 부분은 듣기만 하고 사용해 본 경험은 없어서.. 실습하면서 조금 신기하기도 했고 중간에 약간 헷갈리는 부분도 있어서 곤란했습니다.
  • 모르는 건 예시를 더 찾아보고 투두리스트를 만들 때 날씨 api를 적용했던 것을 떠올리며 전체적으로 어떤 흐름을 갖고 있는지 이해하려고 노력한 것 같습니다.
  • 아직은 자연스럽게 사용은 못하겠지만! 언젠가는 할 수 있겠죠..? ꒰๑˃͈꒳˂͈๑꒱ノ
profile
૮꒰ ྀི〃´꒳`〃꒱ა

0개의 댓글