본문 바로가기
CodeIt_Sprint/비동기_자바스크립트

(3)비동기_자바스크립트_★Promise문법 없이, API 데이터 다루기, Promise 문법 기초 : fetch() , await키워드,json()메소드 , 비동기 + 동기 작업하기 => async 키워드

by 코잼민 2024. 12. 2.

● 이론1_ Promise 문법 없이, 데이터 다루기 :

○ 0단계 : json 데이터 출력법 => JSON객체 프로퍼티 메소드 stringify(jsonData,null,들여쓰기 개수?)

# GPT 설명 (근데 중요한건 아니므로, 대략 읽으면서 끄덕 ㄱ)

JSON.stringify(jsonData, null, 2)은 JavaScript에서 객체(jsonData)를 JSON 형식의 문자열로 변환하는 함수인 JSON.stringify를 사용하는 코드입니다.

각 매개변수의 의미는 다음과 같습니다:

  1. jsonData: JSON 형식으로 변환할 데이터 객체. 예를 들어, { name: "Alice", age: 25 } 같은 객체를 JSON 문자열로 바꿉니다.
  2. null: replacer 매개변수로, 변환 시 특정 속성만 선택하거나 값을 변환하려면 함수를 전달할 수 있습니다. 여기서 null은 기본 동작(모든 속성을 변환)에 해당합니다.
  3. 2: space 매개변수로, 출력되는 JSON 문자열의 들여쓰기 수준을 설정합니다. 숫자 2는 각 단계마다 2칸 들여쓰기를 적용합니다. 이는 사람이 읽기 쉽게 포맷팅된 JSON 문자열을 만드는데 유용합니다.

예제

const jsonData = {
  name: "Alice",
  age: 25,
  skills: ["JavaScript", "React"]
};

const jsonString = JSON.stringify(jsonData, null, 2);
console.log(jsonString);

출력:

{
  "name": "Alice",
  "age": 25,
  "skills": [
    "JavaScript",
    "React"
  ]
}

요약

  • JSON.stringify(jsonData, null, 2)는 jsonData를 JSON 문자열로 변환하며, 사람이 읽기 쉬운 2칸 들여쓰기로 포맷합니다.
  • null과 2는 각각 변환 규칙 없음과 들여쓰기 간격을 설정하는 역할을 합니다.

 

○ 1단계 : 다른 API에서 문자열로 입력된 Json 데이터 불러오기

//API에 저장된 문자열로 입력된 json 데이터라고 가정
const response_2 = `[{ "id": 1, "name": "Jason", "email": "jason@codeitmall.kr", "department": "engineering" }, { "id": 2, "name": "Alice", "email": "alice@codeitmall.kr", "department": "engineering" }, { "id": 3, "name": "Brian", "email": "brian@codeitmall.kr", "department": "marketing" }, { "id": 4, "name": "Erica", "email": "erica@codeitmall.kr", "department": "marketing" }, { "id": 5, "name": "Wilson", "email": "wilson@codeitmall.kr", "department": "sales" }]`;


function getEmployees(callback) {
  console.log('Sending request...');
  //API에서 json 데이터를 불러오는 과정이 1초정도 걸림 => 비동기 메소드로 구현
  setTimeout(() => callback(response_2), 1000);
}

//불러온 데이터를 출력하는 callback 메소드 구현
getEmployees((res)=>{console.log(res)});

○ 2단계 : API로 받은 문자열 json => Parsing 단계를 거쳐, Json 데이터로 변환

//API에 저장된 문자열로 입력된 json 데이터라고 가정
const response_2 = `[{ "id": 1, "name": "Jason", "email": "jason@codeitmall.kr", "department": "engineering" }, { "id": 2, "name": "Alice", "email": "alice@codeitmall.kr", "department": "engineering" }, { "id": 3, "name": "Brian", "email": "brian@codeitmall.kr", "department": "marketing" }, { "id": 4, "name": "Erica", "email": "erica@codeitmall.kr", "department": "marketing" }, { "id": 5, "name": "Wilson", "email": "wilson@codeitmall.kr", "department": "sales" }]`;

function json(strData,callback){
	//1_ parsing해서 json변수에 저장
    const jsonData = JSON.parse(strData);
    
    //2_ 한번에 처리 가능한 메소드가 아니기 때문에, setTimeout()
    //여기서 Parameter : json으로 변환된 Data
	setTimeout(()=>callback(jsonData),1000);    
}

//변환 후의 json데이터를 하나씩 출력하는 데이터

//즉, response_2의 문자열 => str문자열 데이터로 변환후,
//각 인덱스 하나씩 출력하는 메소드가 된거임
json(response_2,(jsonData)=>{console.log(JSON.stringify(jsonData,null,2))});
//여기서 Argument안에 callback의 Argument는 문자열 data가 아닌 json 데이터 이므로, 따로 저렇게 작성해야함

○ 3단계 : json 데이터를 부서별~회원으로 json 정리 메소드

§전략 : strData => jsonData (Parsing 과정) , jsonData => sortData

const response_2 = `[{ "id": 1, "name": "Jason", "email": "jason@codeitmall.kr", "department": "engineering" }, { "id": 2, "name": "Alice", "email": "alice@codeitmall.kr", "department": "engineering" }, { "id": 3, "name": "Brian", "email": "brian@codeitmall.kr", "department": "marketing" }, { "id": 4, "name": "Erica", "email": "erica@codeitmall.kr", "department": "marketing" }, { "id": 5, "name": "Wilson", "email": "wilson@codeitmall.kr", "department": "sales" }]`;

let json_res_2;

//Parsing 과정
function json(string, callback) {
  console.log('Parsing string...');

  json_res_2 = JSON.parse(response_2);
  
  setTimeout(() => callback(json_res_2), 1000);
}

json(response_2,(jsonData)=>{console.log(JSON.stringify(jsonData,null,2))})

//Grouping Sort 과정

function groupEmployees(employees, callback) {
  console.log('Grouping employees...');
  const groupedByDepartment = {}; // 부서별로 그룹화된 데이터를 저장할 객체

  employees.forEach((employee) => {
    const { name, department } = employee;

    // 부서가 아직 객체에 없으면 초기화
    if (!groupedByDepartment[department]) {
      groupedByDepartment[department] = [];
    }

    // 해당 부서 배열에 이름 추가
    groupedByDepartment[department].push(name);
  });

  // 1초 후 콜백 호출
  setTimeout(() => callback(groupedByDepartment), 1000);
}


//부서별 정리된 Json 출력
groupEmployees(json_res_2,(sort_res_2)=>{console.log(JSON.stringify(sort_res_2,null,2))});

○ 4단계 : 1,2,3단계를 한방에 처리해보기 => 중첩 callBack함수(callBack 지옥)

const response = `[{ "id": 1, "name": "Jason", "email": "jason@codeitmall.kr", "department": "engineering" }, { "id": 2, "name": "Alice", "email": "alice@codeitmall.kr", "department": "engineering" }, { "id": 3, "name": "Brian", "email": "brian@codeitmall.kr", "department": "marketing" }, { "id": 4, "name": "Erica", "email": "erica@codeitmall.kr", "department": "marketing" }, { "id": 5, "name": "Wilson", "email": "wilson@codeitmall.kr", "department": "sales" }]`;

let json_res_2;

//1,2,3단계 함수 구현부

//1단계 API에서 data 불러오기
function getData(callback)
{	
	//불러왔다 치고, 
    console.log('Sending request...');
    
    //불러온 data가 strData임
    setTimeout(()=>callback(response),1000);
}

//2단계 불러온 strData => jsonData로 parsing
function parsingData(strData,callback){
	console.log('Parsing string...');
    
    const jsonData = JSON.parse(strData);
    
    setTimeout(()=>callback(jsonData),1000);
}

//3단계
function groupEmployees(employees, callback) {
  console.log('Grouping employees...');
  const groupedByDepartment = {}; // 부서별로 그룹화된 데이터를 저장할 객체

  employees.forEach((employee) => {
    const { name, department } = employee;

    // 부서가 아직 객체에 없으면 초기화
    if (!groupedByDepartment[department]) {
      groupedByDepartment[department] = [];
    }

    // 해당 부서 배열에 이름 추가
    groupedByDepartment[department].push(name);
  });

  // 1초 후 콜백 호출
  setTimeout(() => callback(groupedByDepartment), 1000);
}

//중첩 callBack

getData(()=>json(
response, groupEmployees(jsonData,(sort_data)=>{JSON.stringigy(sort_data,null,2)})
));

중첩 CallBack의 문제점 => 가독성도 ㅈ같고, 디버깅하는데에서도 안좋다고 합디다. => 이러한 것에 대한 대처방안 : Promise 문법 //프로미스나인 생각남

● 이론2_ Promise 객체 정의 :

○ 이론적 정의 :

  • 비동기 작업이 완료되면 값을 알려주는 객체 //다시말하면, API에서 불러온 데이터 || 불러온 StrData를 jsonData로 변환된 값 || jsonData를 특정한 기준으로 분류 후, 재완성된 jsonData등의 시간이 어느정도 필요하여, 비동기작업에 대한 반환 데이터를 다루는 객체를 의미함.
  • 작업이 완료되면 값을 알려줄 것을 '약속'함
  • 먼저, 빈 Promise를 반환 후, 그 Promise객체에 채우겠다는 뜻 //빈 깡통 던져주고, 나중에 사료 채우겠다는 객체

○ Promise 문법 X 비동기 작업 vs Promise 문법 O 비동기 작업 :

  • Promise X 비동기 코드 :
function getData(callback)
{
	const strData <= 불러온 데이터
	setTimeout(()=>callback(strData),1000);
}

function parsing(strData,callback)
{
	const jsonData = JSON.parse(strData);
    
    setTimeout((jsonData)=>{callbakc(jsonData)});
}

getData(()=>parsing(strData,
	console.log(jsonData)
));
  • Promise O 비동기 코드 : { await + fetch()//1단계 } + { await + String 객체 프로퍼티 메소드 json() 메소드//2단계 }
//1단계 : data 불러오기
const strData = await fetch('http://learn.codeit.kr/api/employees');

//2단계 : data Parsing 하기
const jsonData = await strData.json();
//아까와 다르게 string 객체 프로퍼티 메소드 json()메소드 이용

//json의 data 출력
console.log(JSON.stringify(jsonData,null,2));

● 이론3_ Promise 객체 3가지의 상태

- 상태1_ Promise_ Pending 상태 //비동기 작업 진행중 || 미결정인 상태 => 출력 결과

: await 없이 작업만 한 경우의 반환값

Promise {< pending >}

- 상태2_ Promise_ Fulfilled || Rejected 상태 // 비동기 작업 결괏값!

: await 키워드를 붙인 비동기메소드 반환값

◎ 연습문제 :

다음 URL에는 다양한 음식 메뉴가 있습니다(웹 브라우저로 접속해 보세요).

https://learn.codeit.kr/api/menus

fetch() 함수와 await 문법을 사용해서 메뉴 리스트를 가져온 다음 출력하세요.

실습 결과

[
  { type: 'ko', name: '비빔밥' },
  { type: 'ko', name: '삼계탕' },
  { type: 'ko', name: '김치찌개' },
  { type: 'ko', name: '라볶이' },
  { type: 'ch', name: '짜장면' },
  { type: 'ch', name: '탕수육' },
  { type: 'ch', name: '짬뽕' },
  { type: 'jp', name: '라멘' },
  { type: 'jp', name: '스시' },
  { type: 'jp', name: '우동' }
]

- 답 :

// 여기에 코드를 작성하세요.
const strData = await fetch('https://learn.codeit.kr/api/menus');

const jsonData = await strData.json();

console.log(jsonData);

● 이론4_ async 키워드 :

○ 정의 :

await + 비동기작업 메소드 : Promise 객체를 fullfilled || rejected 상태일 때만 반환 되도록 하는 키워드라면, async는 함수 앞에 붙는 키워드로써, 비동기 함수 , 동기 함수가 컴파일러가 왔다갔다 하면서, 원칙적인 작업 순서가 되도록 하는 키워드이다.

○ 예시 코드1 :

const strData = await fetch('https://learn.codeit.kr/api/menus');

const jsonData = await strData.json();

console.log(jsonData);

console.log("1");

console.log("2");

○ 출력 결과:

//비동기 작업 결과이므로, 나중에 출력되는것이 취지에 맞는데,
[
  { type: 'ko', name: '비빔밥' },
  { type: 'ko', name: '삼계탕' },
  { type: 'ko', name: '김치찌개' },
  { type: 'ko', name: '라볶이' },
  { type: 'ch', name: '짜장면' },
  { type: 'ch', name: '탕수육' },
  { type: 'ch', name: '짬뽕' },
  { type: 'jp', name: '라멘' },
  { type: 'jp', name: '스시' },
  { type: 'jp', name: '우동' }
]
//동기 작업 출력 결과
1
2

//시발

○ 예시 코드2 : //예시 코드1의 해결된 코드

async function asyncMethod() {
  const strData = await fetch('https://learn.codeit.kr/api/menus');

  const jsonData = await strData.json();

  console.log(jsonData);
}
asyncMethod();
console.log(1);
console.log(2);

○ 출력 결과:

1
2
[
  { type: 'ko', name: '비빔밥' },
  { type: 'ko', name: '삼계탕' },
  { type: 'ko', name: '김치찌개' },
  { type: 'ko', name: '라볶이' },
  { type: 'ch', name: '짜장면' },
  { type: 'ch', name: '탕수육' },
  { type: 'ch', name: '짬뽕' },
  { type: 'jp', name: '라멘' },
  { type: 'jp', name: '스시' },
  { type: 'jp', name: '우동' }
]


○ async 키워드 관련 규칙 :

  • await 키워드 메소드main 최상위 js or 외부 js에서는 ★async가 지정된 함수 블록 내에서만 작성 가능
  • Arrow_function으로 async 키워드 지정 위치 : () 앞에 붙는다.
const printEmployArrow = async () => {
	const strData = await fetch('...');
    const jsonData = await strData.json();
    console.log(jsonData);
};

● 이론5_ async 상황에서 컴파일러 이동 순서 :

◎ 연습문제2 :

main.js 파일을 실행했을 때 어떤 결과가 출력될까요?

- asyncFunction.js

export async function printEmployees() {
  console.log('2');
  const response = await fetch('https://learn.codeit.kr/api/employees');
  console.log('3');
  const data = await response.json();
  console.log('4');
}

- main.js

import { printEmployees } from './asyncFunctions.js';

console.log('1');

printEmployees();

console.log('5');

console.log('6');

- 알아야 할 개념 :

=> 컴파일러는 async만 보고 왔다리 갔다리가 아닌, 비동기 함수의 반환 값인 Promise 객체의 상태를 보고 왔다갔다 하는 것이다.

=> 그럼 시발 async 키워드는 왜 있음? : 약간 이정표 느낌 정도인것 같다. //요안에 비동기함수 있으니깐 시발아 Promise 객체 확인하고 가 하는 김부선 느낌임

- 답 :

1
2
5
6
3
4