2차 공부/TIL

24.07.18 async try {await} catch() / Promise

공대탈출 2024. 7. 18. 18:01

어제 너무 긴 html파일의 style, script를 분리하여 모듈화 시켰다.

한 html에서 변수에 데이터를 저장하여 해당 데이터를 특정 기능에서 사용할 때에는 문제가 없었지만, 모듈화를 시켜 어떤 데이터를 다른 모듈에서 사용할 때 오류가 발생했다.

 

페이지의 멤버데이터는 firebase에 수동으로 저장해두었고, 해당 데이터를 페이지가 불러와지고 난 뒤 처음 한번만 데이터를 GET하여 memberArray에 저장하고, memberArray를 필요한 곳에서 사용해야했다.

    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
    import { getFirestore, 
        collection,
        getDocs,
        doc,
        updateDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

    let memberArray = [];
    export async function firebaseFn() {
        // Firebase SDK 라이브러리 가져오기
        const firebaseConfig = {
            apiKey: "apiKey",
            authDomain: "authDomain",
            projectId: "projectId",
            storageBucket: "storageBucket",
            messagingSenderId: "messagingSenderId",
            appId: "appId",
            measurementId: "measurementId",
        };
        // Firebase 인스턴스 초기화
        const app = initializeApp(firebaseConfig);
        const db = getFirestore(app);
        let docs = await getDocs(collection(db, "members"))
        docs.forEach((doc) => {
            memberArray.push(doc.data());
        });
    }
    export default memberArray;

    /**
     * @param memberId - 위치를 얻길 원하는 멤버의 id
     * @return 지정한 멤버의 firestore 위치
     */
    export function getRef(memberId) {
        return doc(db, "members", `${memberId}`);
    }

처음 코드는 대략 이러했다. 빈 memberArray를 만들고 async await를 사용하여 가져온 데이터를 memberArray에 push하여 export default로 내보내는 형식이었다.

 

import memberArray from "./firebase.js";
export function cardFn() {
	....
}

그렇게 내보낸 memberArray를 다른 함수에서 import하여 사용했는데, 간헐적으로 memberArray에 메서드를 사용하는 부분에서 에러가 발생하였다.

Uncaught TypeError: memberArray.push() is not a function

정확히 기억이 나진 않지만 이러한 오류였다.

 

아마 서버에서 데이터를 완전히 가져오기 전에 해당 부분을 인터프리터가 읽어들이면서 배열이라고 받아들이지 못하여 생긴 오류가 아닐까 라는 추측을 한다.

그래서 서버에서 데이터가 완전히 받아와진 뒤 memberArray를 다른 모듈들에게 전해주는 과정이 필요했다.

 

    import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
    import { getFirestore, 
        collection,
        getDocs,
        doc,
        updateDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

    export let db;
    export async function firebaseFn() {
        let memberArray = [];
        // Firebase SDK 라이브러리 가져오기
        const firebaseConfig = {
            apiKey: "apiKey",
            authDomain: "authDomain",
            projectId: "projectId",
            storageBucket: "storageBucket",
            messagingSenderId: "messagingSenderId",
            appId: "appId",
            measurementId: "measurementId",
        };
        // Firebase 인스턴스 초기화
        const app = initializeApp(firebaseConfig);
        db = getFirestore(app);
        try {
            let docs = await getDocs(collection(db, "members"))
            docs.forEach((doc) => {
                memberArray.push(doc.data());
            });
            return memberArray
        } catch(err) {
            console.log('에러 발생, ', err)
            return;
        }
    }

    /**
     * @param memberId - 위치를 얻길 원하는 멤버의 id
     * @return 지정한 멤버의 firestore 위치
     */
    export function getRef(memberId) {
        return doc(db, "members", `${memberId}`);
    }

    /** 
     * 지정한 위치의 데이터를 변경합니다.
     * @param ref - 접근할 멤버의 firestore 위치
     * @param data - 수정할 데이터 */
    export function update(ref, data) {
        updateDoc(ref, data);
    }

async로 만들어진 function에서 getDocs가 완료될 때까지 await하는데, 통신이 완료되면 해당 데이터를 수정하여 memberArray로 더해주고, memberArray를 리턴하는 형식이다.

import { firebaseFn } from "./firebase.js"
import { cardFn } from "./cards.js"
import { modalFn } from "./modal.js"
import { setScrollEvent } from "./title.js"

firebaseFn().then((memberArray)=> {
    cardFn(memberArray)
    modalFn(memberArray)
})

setScrollEvent();

그렇게 리턴된 memberArray는 then()메서드 안에서 인자로서 cardFn과 modalFn에게 전해지고 유효한 형식인 상태이기에 오류가 생기지 않는 것이다.

 

이번 기회로 Promise에 대해 공부해 보았다.

 

MDN - Promise

Promise가 제공하는 resolve, reject를 사용하면 비동기 처리방식을 동기적으로 처리할 수 있으면 이는 위에서 변경한 async await, try catch구문과 비슷하지만 다른 형태이다.

 

function a() {new Promise((resolve, reject) => {
    let data = fetch('https://todoList/1')
    resolve(data)
})
}
a().then((data) => {
    console.log(data)
    throw new Error('에러 발생')
}).catch((err) => {
    console.log('에러메시지= ', err)
})

함수 a가 실행되면 새로운 Promise객체를 실행하고, 어떤 링크로부터 data를 받아와 성공할 시 data를 리턴하는 형태이다.

그리고나서 then으로 resolve안 data를 인자로 사용하며, 에러 발생시 에러메시지를 만든다. 만약 에러가 발생한다면 catch구문을 통해 에러메시지가 콘솔창에 뜨게된다.

 

function b() {new Promise((resolve, reject) => {
    let data = fetch('https://todoList/1')
    if (data) {
        resolve(data)
    }
    reject(new Error("데이터 불러오기 실패"))
})
}
b().then((data) => {
    console.log(data)
}).catch((err) => {
    console.log('에러메시지= ', err)
})

함수 b가 실행되면 위와같이 data를 가져오는데, 만약 데이터가 정상적으로 들어와 값이 존재한다면 resolve하고, 아니라면 reject로 에러메시지를 내보내는 형식이다.

then안의 data는 resolve를 통해 받은 data이고 그 안에서 사용이 가능하다. 또한 catch구문 안의 err는 reject에서 새롭게 만들었던 에러메시지이며 catch 구문 안에서 사용가능하다.


이전 리액트를 공부할 때에도 Promise를 제대로 이해하지 못하고 적당히 넘겼었는데, 이번 기회로 자세히 알게되어 다행이라고 생각한다.

js언어에서 매우 중요한 부분이라고 생각하기 때문에 기회가 된다면 더 자세히 알아볼 예정이다. 

 


 

 

자바스크립트 Promise 쉽게 이해하기

(중급) 자바스크립트 입문자를 위한 Promise 설명. 쉽게 알아보는 자바스크립트 Promise 개념, 사용법, 예제 코드. 예제로 알아보는 then(), catch() 활용법

joshua1988.github.io