웹 서비스 개발(FB,BE,SERVER,DB)/Javascript

4. 비동기처리

Zoo_10th 2024. 3. 6.

1. JSON

JSON (JavaScript Object Notation)은 데이터 교환을 위한 간단하고 경량의 텍스트 기반 구조다. JSON은 읽기 쉬운 key-value 쌍 형식을 사용하여 데이터를 표현한다. 그 주요 특징은 다음과 같다.

1) JavaScript Object Notation (자바스크립트 객체 표기법): JSON은 원래 JavaScript 언어의 객체 표기법을 기반으로 하여 개발되었다.

2) 간단한 데이터 교환 형식: JSON은 서버와 클라이언트 간 또는 시스템 간의 데이터 전송을 위해 사용되며, 그 구조가 간단하고 명확하여 데이터 교환을 쉽게 할 수 있다.

3) 경량 텍스트 기반의 구조: JSON은 텍스트 기반으로, 이는 대부분의 프로그래밍 언어에서 쉽게 읽고 쓸 수 있다는 것을 의미다. 이로 인해 데이터를 교환하는 데 있어 효율적이다.

4) 읽기 쉬운 key : value 형식: 데이터는 key와 value의 쌍으로 구성되며, 이 형식은 사람이 읽고 이해하기 쉽다.

5) 특정 언어에 제한적이지 않고 독립적인 방식: JSON은 JavaScript에 국한되지 않고 다양한 프로그래밍 언어에서 사용할 수 있다. 이는 JSON이 프로그래밍 언어 독립적인 데이터 포맷이라는 것을 의미한다.

1-1. JSON 생성

1) Object ➔ JSON (stringify) 

JSON.stringify() 메소드는 JavaScript 객체 (또는 값)를 JSON 문자열로 변환한다.

json = JSON.stringify(['apple', 'banana']);
console.log(json); // 출력: '["apple","banana"]'

문자열 배열 ['apple', 'banana']를 JSON 문자열 '["apple","banana"]'로 변환한다.

2) JSON ➔ Object (parse)

JSON.parse() 메소드는 JSON 문자열을 JavaScript 객체로 변환한다.

let json = JSON.stringify(['apple', 'banana']);
console.log(json); // 출력: '["apple","banana"]'

const obj = JSON.parse(json);
console.log(obj); // 출력: ['apple', 'banana']

먼저 배열 ['apple', 'banana']를 JSON 문자열로 변환한 후, 다시 이 JSON 문자열을 JavaScript 배열로 변환하여 출력한다.

3) JSON 생성 시 제외되는 타입:

JavaScript의 JSON.stringify() 함수는 객체를 JSON 문자열로 변환할 때 일부 타입을 자동으로 제외한다. 이 예시에서는 두 가지 타입이 제외된다.

 - 함수 : JavaScript 함수는 JSON으로 변환되지 않습니다. 이 예시에서 jump 속성(함수)는 결과 JSON에 포함되지 않는다.

 - 심볼(Symbol) : 심볼 또한 JSON으로 변환되지 않는다. symbol 속성은 결과 JSON에서 제외된다.

const rabbit = {
  name: 'tori',
  color: 'white',
  size: null,
  birthDate: new Date(),
  jump: () => {
    console.log(`${rabbit.name} can jump!`);
  },
  symbol: Symbol('id')
};

let json = JSON.stringify(rabbit);
console.log(json); // 출력: {"name":"tori","color":"white","size":null,"birthDate":"2024-01-22T12:00:00.000Z"}

birthDate는 Date 객체이며, 이는 JSON으로 변환될 때 문자열로 변환된다.

4) JSON 생성 시 사용할 속성 지정 

JSON.stringify() 함수의 두 번째 인자로 속성 이름의 배열을 전달하여, JSON으로 변환하고자 하는 특정 속성을 지정할 수 있다.

let json = JSON.stringify(rabbit, ['name', 'color', 'size']);
console.log(json); // 출력: {"name":"tori","color":"white","size":null}

name, color, size 속성만이 JSON으로 변환된다. birthDate, jump, symbol 속성은 결과 JSON에 포함되지 않는다.

2. 비동기 처리

2-1. Callback

콜백 함수는 특정 작업(예: API 호출, 타이머 등)이 완료된 후에 실행되도록 설계된 함수다. 이런 함수는 다른 함수의 매개변수로 전달되어, 그 함수 내부에서 적절한 시점에 호출된다.

1) 기본 예시

console.log(1);
setTimeout(delayPrint, 1000);
console.log(3);

function delayPrint() {
  console.log(2);
}

delayPrint 함수는 콜백 함수로서, setTimeout에 의해 1초 후에 호출된다. 따라서 출력은 1, 3, 그리고 1초 후에 2 순서로 이루어진다.

2) Callback 함수 예제

class UserStorage {
    // loginUser 메소드는 ID와 비밀번호를 받아 로그인을 시도합니다.
    loginUser(id, password, onSuccess, onError) {
        // 비동기 작업(여기서는 setTimeout으로 모의 구현)
        setTimeout(() => {
            // 성공 조건을 확인합니다.
            if ((id === 'java' && password === 'script') ||
                (id === 'call' && password === 'back')) {
                onSuccess(id); // 성공 시 onSuccess 콜백 호출
            } else {
                onError(new Error('not found')); // 실패 시 onError 콜백 호출
            }
        }, 2000);
    }

    // getRoles 메소드는 사용자 ID를 받아 권한을 확인합니다.
    getRoles(id, onSuccess, onError) {
        // 비동기 작업
        setTimeout(() => {
            // 사용자 ID에 따른 권한 할당
            if (id === 'java') {
                onSuccess({ id: 'java', role: 'admin' });
            } else if(id === 'call') {
                onSuccess({ id: 'call', role: 'manager' });
            } else {
                onError(new Error('no access')); // 권한 없음 에러 처리
            }
        }, 1000);
    }
}

fetchUserData 함수는 비동기적으로 사용자 데이터를 가져오는 함수다. 실제 네트워크 요청을 모의하기 위해 setTimeout을 사용했다. 이 함수는 userId와 콜백 함수 callback을 매개변수로 받는다. 데이터가 준비되면 callback 함수가 호출되며, 이 때 displayUserName 함수가 callback으로 전달되어 사용자 이름을 콘솔에 출력한다.

2-2. Promise

Promise는 JavaScript에서 비동기 작업을 용이하게 처리하기 위해 도입된 객체다. 콜백 함수의 복잡성과 중첩 문제를 해결하는 데 도움이 되며, 코드의 가독성과 유지 보수성을 크게 향상시킨다.

2-2-1. Promise의 주요 개념

1) 비동기 작업을 위한 객체: Promise는 비동기 작업을 캡슐화하는 객체다. 비동기 작업의 결과(성공 또는 실패)를 나타낸다.

2) 콜백 함수의 대체: 전통적인 콜백 함수 대신 사용되며, 코드의 중첩을 줄여준다.

3) 성공과 실패 처리

 - resolve: 비동기 작업이 성공적으로 완료되었을 때 호출됩니다. resolve 함수에 전달된 값은 Promise 사용자에게 전달된다.

 - reject: 비동기 작업 중 에러가 발생했을 때 호출됩니다. reject 함수에 전달된 에러는 Promise 사용자에게 전달된다.

2-2-2. Promise의 상태(State)

1) Pending (수행중): 초기 상태, 성공 또는 실패가 아직 결정되지 않은 상태다.

2) Fulfilled (성공/완료): 작업이 성공적으로 완료되어 resolve가 호출된 상태다.

3) Rejected (실패/거부): 작업 중 에러가 발생하여 reject가 호출된 상태다.

2-2-3. 데이터 제공자(Producer)와 사용자(Consumer)

1) Producer: Promise를 생성하고, 비동기 작업을 수행한 후 결과(resolve 또는 reject)를 제공한다.

2) Consumer: Promise의 결과를 사용합니다. .then(), .catch(), .finally() 메소드를 통해 결과를 처리한다.

 - Promise 기본 사용법

const myPromise = new Promise((resolve, reject) => {
    // 비동기 작업을 수행하는 코드
    const condition = /* 조건 */;
    if (condition) {
        resolve('성공'); // 작업 성공 시
    } else {
        reject('실패'); // 작업 실패 시
    }
});

// Promise의 결과 사용
myPromise.then((result) => {
    console.log(result); // 성공 시 처리
}).catch((error) => {
    console.error(error); // 실패 시 처리
});

 - Promise 사용 예시

class UserStorage {
    // loginUser 메소드는 ID와 비밀번호를 받아 Promise를 반환합니다.
    loginUser(id, password) {
        return new Promise((resolve, reject) => {
            // 비동기 작업
            setTimeout(() => {
                // 성공 조건을 확인합니다.
                if ((id === 'java' && password === 'script') ||
                    (id === 'call' && password === 'back')) {
                    resolve(id); // 성공 시 resolve 호출
                } else {
                    reject(new Error('not found')); // 실패 시 reject 호출
                }
            }, 1000);
        });
    }

    // getRoles 메소드는 사용자 ID를 받아 권한을 확인하는 Promise를 반환합니다.
    getRoles(id) {
        return new Promise((resolve, reject) => {
            // 비동기 작업
            setTimeout(() => {
                // 사용자 ID에 따른 권한 할당
                if (id === 'java') {
                    resolve({ id: 'java', role: 'admin' });
                } else if (id === 'call'){
                    resolve({ id: 'call', role: 'manager' });
                } else {
                    reject(new Error('no access')); // 권한 없음 에러 처리
                }
            }, 1000);
        });
    }
}

 

2-3. 동기와 비동기

2-3-1. 동기적 수행 (Synchronous Execution)

1) 한번에 하나씩 순서대로 작업 처리: 동기적 수행에서는 한 작업이 완료되어야만 다음 작업으로 넘어간다. 즉, 작업은 순차적으로 진행된다.

2) 블로킹 방식: 현재 실행 중인 작업이 완료될 때까지 기다리기 때문에, 해당 작업이 끝나기 전까지 다른 작업을 시작할 수 없다. 이를 '블로킹(blocking)'이라고 한다.

3) 예시: 데이터베이스 쿼리를 수행하고 결과가 반환될 때까지 기다리는 경우, 파일을 읽고 읽기 작업이 완료될 때까지 다음 코드를 실행하지 않는 경우 등이 있다.

function syncFunction() {
    console.log("작업 1 시작");
    console.log("작업 1 완료");

    console.log("작업 2 시작");
    // 작업 2 수행 (가정)
    console.log("작업 2 완료");
}

console.log("동기적 수행 시작");
syncFunction();
console.log("동기적 수행 완료");

2-3-2. 비동기적 수행 (Asynchronous Execution)

1) 선행 작업 완료를 기다리지 않고 다음 작업 수행: 비동기적 수행에서는 하나의 작업이 끝나기를 기다리지 않고 다음 작업을 바로 시작할 수 있다.

2) 논블로킹 방식: 현재 작업이 완료되지 않았더라도 다른 작업을 계속 진행할 수 있다. 이를 '논블로킹(non-blocking)'이라고 한다.

3) 예시: 웹페이지에서 서버에 데이터를 요청하고, 서버의 응답을 기다리는 동안 다른 스크립트를 실행하는 경우, 파일을 비동기적으로 읽으면서 동시에 다른 코드를 실행하는 경우 등이 있다.

function asyncFunction() {
    console.log("비동기 작업 1 시작");
    setTimeout(() => {
        // 비동기 작업 1 수행 (가정)
        console.log("비동기 작업 1 완료");
    }, 2000); // 2초 후에 완료

    console.log("비동기 작업 2 시작");
    setTimeout(() => {
        // 비동기 작업 2 수행 (가정)
        console.log("비동기 작업 2 완료");
    }, 1000); // 1초 후에 완료
}

console.log("비동기 수행 시작");
asyncFunction();
console.log("비동기 수행 중..."); // 비동기 작업들이 완료되기 전에 이 로그가 출력됩니다.

2-3-3. 동기와 비동기의 차이점

1) 응답성: 동기적 수행은 작업이 차례대로 진행되기 때문에, 한 작업이 많은 시간을 소요할 경우 전체 프로세스의 응답성이 저하될 수 있다. 반면, 비동기적 수행은 여러 작업이 동시에 진행될 수 있어 응답성이 좋다.

2) 자원 활용: 비동기적 수행은 시스템 자원을 보다 효율적으로 사용할 수 있다. 예를 들어, I/O 작업이 진행되는 동안 CPU는 다른 작업을 수행할 수 있다.

3) 복잡성: 비동기적 수행은 동기적 수행에 비해 프로그래밍 복잡성이 증가할 수 있다. 비동기 작업의 결과 처리를 위해 콜백 함수, 프로미스, async/await 등의 방법이 사용되며, 이들을 효과적으로 관리해야 한다.

728x90

'웹 서비스 개발(FB,BE,SERVER,DB) > Javascript' 카테고리의 다른 글

DOM API  (0) 2024.03.06
3. 함수  (0) 2024.03.06
2. 자료형(DataType)  (1) 2024.03.06
1. JavaScript  (0) 2024.03.04

댓글