본문 바로가기

멋쟁이사자처럼_부트캠프

[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 26일차 JavaScript 비동기처리

JavaScript 객체 지향 프로그래밍(OOP) 구현

1. 프로토타입 (Prototype)

  • 정의: 자바스크립트는 프로토타입 기반 객체 지향 언어입니다. 모든 객체는 자신의 부모 역할을 하는 프로토타입 객체를 참조하며, 이로 인해 프로토타입 체인을 통해 상속이 가능합니다.
  • 특징:
    1. Prototype Property:
      • 함수 객체는 기본적으로 prototype 속성을 가집니다.
      • 이 속성은 해당 함수로 생성된 객체들이 상속받는 공유 속성을 정의합니다.
    2. 프로토타입 체인:
      • 객체는 프로토타입 체인을 따라 필요한 속성이나 메서드를 찾습니다.
      • 체인은 Object.prototype에서 끝납니다.
  • 사용 예제:
// 생성자 함수
function Person(name) {
    this.name = name;
}

// 프로토타입에 메서드 추가
Person.prototype.sayHello = function () {
    console.log(`Hello, my name is ${this.name}`);
};

// 인스턴스 생성
const person1 = new Person("Alice");
person1.sayHello(); // Hello, my name is Alice

// 프로토타입 체인
console.log(person1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true

 


2. 클래스 (Class)

- 클래스라는 기능은 C++ , Java, C#, PHP 등의 다른 프로그래밍 언어에는 있는 기능인 데 자바스크립트에는 없었기 때문에 예전 자바스크립트 (ES5) 에서는 클래스 문 법이 따로 없었기 때문에 위에서 작성한 코드처럼 객체 생성자 함수를 사용하여 비슷한 작업을 구현해왔다. ES6부터 클래스 문법이 추가 되었다.

  • 정의: ES6(ECMAScript 2015)부터 추가된 클래스 문법은 프로토타입 기반 상속을 더 간결하고 읽기 쉽게 표현하는 방식입니다. 내부적으로는 여전히 프로토타입을 사용합니다.
  • 특징:
    1. 구문적 설탕 (Syntactic Sugar):
      • 클래스는 기존 프로토타입 기반 코드를 보기 좋게 만든 문법입니다.
    2. 정적 메서드 및 상속:
      • static 키워드를 사용하여 클래스 자체에 메서드를 정의할 수 있습니다.
      • extends 키워드로 클래스를 상속할 수 있습니다.
    3. 생성자:
      • 클래스의 인스턴스를 초기화하기 위해 constructor 메서드를 사용합니다.
  • 사용 예제:
class Person {
    constructor(name) {
        this.name = name;
    }

    // 인스턴스 메서드
    sayHello() {
        console.log(`Hello, my name is ${this.name}`);
    }

    // 정적 메서드
    static describe() {
        console.log("This is a Person class.");
    }
}

// 인스턴스 생성
const person1 = new Person("Bob");
person1.sayHello(); // Hello, my name is Bob

// 정적 메서드 호출
Person.describe(); // This is a Person class.

// 상속
class Developer extends Person {
    constructor(name, language) {
        super(name); // 부모 클래스의 생성자 호출
        this.language = language;
    }

    sayLanguage() {
        console.log(`${this.name} codes in ${this.language}`);
    }
}

const dev = new Developer("Alice", "JavaScript");
dev.sayHello(); // Hello, my name is Alice
dev.sayLanguage(); // Alice codes in JavaScript

차이점 요약

특징 프로토타입 클래스
도입 시기 ES5 이전부터 존재 ES6부터 도입
문법 복잡하고 장황한 코드를 작성 간결하고 직관적인 문법 제공
상속 프로토타입 체인으로 구현 extends 키워드를 사용해 쉽게 상속
정적 메서드 직접 추가해야 함 static 키워드로 간단히 정의 가능
실행 방식 함수와 prototype 속성으로 동작 내부적으로 프로토타입을 기반으로 동작
가독성 낮음 높음

 

 

결론적으로, 프로토타입은 자바스크립트의 근본적인 객체 지향 방식이며, 클래스는 이를 더 직관적이고 효율적으로 사용할 수 있도록 제공된 문법입니다. 클래스 문법은 프로토타입의 복잡함을 숨기고 더 간편한 사용을 가능하게 합니다.


 

asynchronous communication : 말그대로 비동기 통신. (synchronous : 동시에 발생하는)

JavaScript에서 비동기 처리

JavaScript는 기본적으로 단일 스레드(single-thread) 환경에서 동작하지만, 비동기 처리를 통해 시간 소모가 큰 작업(예: 네트워크 요청, 파일 읽기)을 효율적으로 처리할 수 있습니다. 비동기 처리는 이벤트 루프(Event Loop) 메커니즘에 의해 관리됩니다.

 

JavaScript는 비동기 실행 모델을 사용하기 때문에, 비동기 작업이 실행되면서 결과를 기다리지 않고 나머지 코드를 바로 실행합니다. 따라서, 특정 작업이 완료된 후 처리해야 할 작업을 함께 지정하지 않으면 예상하지 못한 동작이나 에러가 발생할 수 있습니다. 비동기 호출할 때는 비동기로 수행한 코드가 끝난 후 처리해야 하는 문장들을 함께 보내야 합니다.

-> 비동기 작업의 완료 시점에 수행해야 할 작업을 명시적으로 지정해야 한다는 의미

 

1. 동기코드

//"작업 완료"는 항상 "작업 시작" 이후에 실행됨.
function task() {
    console.log("작업 시작");
    console.log("작업 완료");
}
task();

//작업 시작
//작업 완료

 

2. 비동기 코드

function asyncTask() {
    setTimeout(() => {
        console.log("비동기 작업 완료");
    }, 1000);
}

console.log("작업 시작");
asyncTask();
console.log("작업 완료");

//작업 시작
//작업 완료
//비동기 작업 완료

비동기 작업(setTimeout)은 바로 실행되지 않고, 1초 후에 완료됩니다. 따라서 "작업 완료"는 비동기 작업이 끝나기 전에 실행됩니다.

 

1. 비동기 처리의 주요 방법

1.1. 콜백 함수 (Callback Function)

비동기 작업이 완료된 후 실행될 함수를 인수로 전달하는 방식입니다.

  • 특징:
    • 가장 기본적인 비동기 처리 방식.
    • 하지만 콜백이 중첩되면 "콜백 지옥(Callback Hell)"로 인해 코드가 읽기 어려워집니다.
  • 예제:
function fetchData(callback) {
    setTimeout(() => {
        callback("Data loaded");
    }, 1000);
}

fetchData((data) => {
    console.log(data); // "Data loaded"
});
  • 콜백 함수는 콜백 지옥 문제를 발생시키는 문제가 있다: 중첩 구조로 인해 읽기 어렵고, 유지보수가 힘들어짐.
setTimeout(() => {
    console.log("1초 후 실행");
    setTimeout(() => {
        console.log("2초 후 실행");
        setTimeout(() => {
            console.log("3초 후 실행");
        }, 1000);
    }, 1000);
}, 1000);

1.2. 프로미스 (Promise)

ES6에서 도입된 Promise는 비동기 작업의 성공 또는 실패를 처리하는 객체입니다.

  • 특징:
    • 콜백보다 읽기 쉽고, 체인(.then, .catch)을 통해 작업을 순차적으로 처리 가능.
    • resolve와 reject를 통해 비동기 작업의 결과를 반환.
  • 예제:
const fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data loaded");
        }, 1000);
    });
};

fetchData()
    .then((data) => {
        console.log(data); // "Data loaded"
    })
    .catch((error) => {
        console.error(error);
    });
  • 체이닝:
fetchData()
    .then((data) => {
        console.log(data); // "Data loaded"
        return "Next Step";
    })
    .then((next) => {
        console.log(next); // "Next Step"
    })
    .catch((error) => {
        console.error(error);
    });

1.3. async/await

ES8에서 도입된 async/await는 프로미스를 기반으로 작성되며, 비동기 코드를 동기 코드처럼 읽기 쉽게 작성할 수 있습니다.

  • 특징:
    • async 함수는 항상 프로미스를 반환합니다.
    • await 키워드는 프로미스가 처리될 때까지 기다립니다.
  • 예제:
const fetchData = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data loaded");
        }, 1000);
    });
};

async function getData() {
    try {
        const data = await fetchData();
        console.log(data); // "Data loaded"
    } catch (error) {
        console.error(error);
    }
}

getData();
  • 에러 처리:
    • try/catch 블록을 사용하여 에러를 처리합니다.
async function getData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (error) {
        console.error(error);
    }
}

2. 비동기 처리의 실행 메커니즘

2.1. 이벤트 루프(Event Loop)

JavaScript의 비동기 처리는 이벤트 루프에 의해 관리됩니다.

  1. 콜 스택(Call Stack):
    • 동기 코드는 스택에 쌓이고, 실행 후 제거됩니다.
  2. 태스크 큐(Task Queue):
    • 비동기 작업의 콜백 함수는 완료된 후 태스크 큐에 들어갑니다.
  3. 이벤트 루프(Event Loop):
    • 콜 스택이 비어 있으면 태스크 큐에서 작업을 가져와 실행합니다.

2.2. 실행 흐름 예제

console.log("Start");

setTimeout(() => {
    console.log("Timeout");
}, 1000);

console.log("End");
  • 실행 순서:
    1. "Start" 출력 (콜 스택).
    2. setTimeout 실행 → Web API로 작업 전달.
    3. "End" 출력 (콜 스택).
    4. 1초 후, 태스크 큐에서 콜백(console.log("Timeout"))을 가져와 실행.
  • 출력 결과:
Start
End
Timeout

3. 비동기 처리의 주요 사례

3.1. 네트워크 요청

fetch("https://api.example.com/data")
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.error(error));

3.2. 파일 읽기 (Node.js 예제)

const fs = require("fs");

fs.readFile("example.txt", "utf8", (err, data) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(data);
});

비동기 처리 요약

기법 특징 장점 단점
콜백 함수의 인수로 콜백 전달 단순한 비동기 처리 가능 콜백 지옥 문제로 가독성 저하
프로미스 비동기 작업을 resolve, reject로 처리 체인 형태로 가독성 향상 코드가 여전히 길어질 수 있음
async/await 프로미스를 동기처럼 처리 가독성 최상, 직관적인 코드 작성 가능 에러 처리 시 try/catch가 필요함

 

결론

비동기 작업이 많아질수록 async/await가 가장 직관적이고 코드 가독성을 높이는 데 유리합니다. 그러나 간단한 작업에는 프로미스 또는 콜백을 활용할 수도 있습니다.

 

 

 

비동기에 대한 이해는 아래 글을 참고해도 좋다.

https://springfall.cc/article/2022-11/easy-promise-async-await

 

[Javascript] 비동기, Promise, async, await 확실하게 이해하기

초보자 입장에서 헷갈리기 쉬운 자바스크립트의 Promise 에 대해 낱낱이 파헤칩니다. 더 나아가 async와 await을 올바르게 사용하는 법까지 소개합니다.

springfall.cc