Skip to main content

Command Palette

Search for a command to run...

Promise 제대로 사용하는 법 — 동기값 반환 때문에 발생하는 .then 오류와 해결

Published
•2 min read•View as Markdown

들어가며

JavaScript에서 비동기 작업을 다룰 때 Promise를 일관되게 반환하는 것이 중요합니다.

함수가 Promise 대신 동기값(또는 undefined)을 반환하면 호출부에서 .then/.catch를 호출할 수 없어 런타임 오류가 납니다.

문제 사례 (실제 오류)

  • 에러: Uncaught TypeError: User.isDuplicateLogin(...).then is not a function

  • 원인: isDuplicateLogin이 Promise를 반환하지 않고 boolean 또는 undefined를 반환했기 때문.

나쁜 예 (동기 boolean 반환 — 오류 발생)

// 잘못된 패턴: 동기값을 반환하면 호출부에서 .then을 쓸 수 없음
function isDuplicateLogin(userId) {
    // 서버 호출을 했지만 결과를 반환(return)하지 않음
    axiosApi.get(`/api/user/${userId}/auth`).then((res) => {
        return !!res?.results?.hasActiveSessions; // 이 값은 then 핸들러 내부에서만 사용되고 함수는 undefined 반환
    });
}

올바른 예: Promise를 반환하도록 고치기

  • 핵심: 내부에서의 비동기 체인을 반드시 return 하여 호출자에게 Promise를 돌려준다.
// 권장: 항상 Promise<boolean>을 반환
function isDuplicateLogin(userId) {
    return axiosApi
        .get(`/api/user/${encodeURIComponent(userId)}/auth`)
        .then((res) => {
            if (!res || typeof res.results?.hasActiveSessions === 'undefined') {
                // 서버 응답이 기대치와 다르면 에러를 던져 호출자가 판단하게 함
                const e = new Error('NO_DUPLICATE_INFO');
                e.code = 'NO_DUPLICATE_INFO';
                throw e;
            }
            // boolean으로 표준화해서 반환
            return !!res.results.hasActiveSessions;
        });
}

호출부 예시 1 — Promise 체인 (.then/.catch/.finally)

// 로그인 성공 후 중복확인 흐름 (async/await 없이)
User.isDuplicateLogin(userId)
    .then(function (isDup) {
        if (isDup) {
            const proceed = confirm('다른 세션이 있습니다. 계속하시겠습니까?');
            if (!proceed) throw { handled: true }; // 체인 중단 신호
        }
        // 중복 검사를 통과한 이후 처리
        // 상태 설정, 로컬 정리, 리다이렉트 등
    })
    .catch(function (err) {
        if (err && err.handled) return;
        if (err?.code === 'NO_DUPLICATE_INFO') {
            alert('서버에서 중복 여부를 확인할 수 없습니다.');
            return;
        }
        // 실제 오류 처리
        console.error(err);
        alert('중복 확인 중 오류가 발생했습니다.');
    })
    .finally(function () {
        // 로딩 숨김/플래그 복구는 항상 finally에서
        utilHideLoadingIndicator();
        User.isLoginPending = false;
    });

호출부 예시 2 — async/await 스타일

async function handleLoginFlow(userId) {
    try {
        const isDup = await User.isDuplicateLogin(userId);
        if (isDup) {
            const proceed = confirm('다른 세션이 있습니다. 계속하시겠습니까?');
            if (!proceed) return;
        }
        // 이후 처리
    } catch (err) {
        if (err?.code === 'NO_DUPLICATE_INFO') {
            alert('서버에서 중복 여부를 확인할 수 없습니다.');
            return;
        }
        if (err?.name === 'CanceledError' || err?.code === 'ERR_CANCELED') return;
        console.error(err);
        alert('중복 확인 중 오류가 발생했습니다.');
    } finally {
        utilHideLoadingIndicator();
        User.isLoginPending = false;
    }
}

이번에 배운점!

  • 항상 Promise를 반환(return)하라. (axios/Fetch는 Promise, jQuery의 $.ajax는 jqXHR(then 사용 가능)임을 유의)

  • 함수는 결과를 boolean/도메인 값으로 표준화해 반환하거나, 오류는 던져(callee에서 throw) 호출자가 판단하도록 하라.

  • 에러를 덮어쓰지 말고 필요한 경우 코드/메시지를 붙여 throw하라(구분용).

  • 상태 복구(utilHideLoadingIndicator, isLoginPending 등)는 finally에서 처리해 레이스 컨디션을 막아라.

  • 취소(AbortController)와 같은 케이스는 호출부와 인터셉터에서 일관되게 무시하도록 처리하라.

결론

  • Promise를 제대로 반환하고 에러 전파/정규화를 명확히 하면 .then is not a function 같은 오류를 예방할 수 있다.

  • 호출부는 반환된 Promise를 기다리거나 catch에서 적절한 처리를 하면 안전한 비동기 흐름을 만들 수 있다.

More from this blog

Bundling Fundamentals

들어가며 프론트엔드에서 번들링을 이해하는 과정은 웹 성능 최적화를 위해서 꼭 짚고 넘어가야할 부분이다.토스에서 작성해둔 bundling Fundamentals 과정을 통해서 번들링이 '어디서 어떻게' 일어나는지 함께 이해해보자! 번들링이란 번들링은 여러개의 웹 개발에 필요한 리소스 파일들을 하나 또는 몇 개의 파일로 묶는 작업을 얘기하고, 이렇게 묶인 파일

Mar 30, 20261 min read

진짜 git 아직도 모르냐!!!

들어가며 진짜 회사 입사한지 일년이 됐지만 아직까지 git을 초보자처럼 쓰고있는 내 모습에 너무 황당해서 쓰는 블로그.. 회사에서는 그냥 git clone해서 애초에 git fetch, git pull, git push, git commit, git add 등등 명령어만 남발하면서 충돌 없이 최대한 조심스럽게 익숙한 방식대로만 써왔음 에러나면 무조건 ai한테 왜이래 남발하면서 충돌해결하고 rebase, merge 차이도 잘 모르겠고,, 심지어 ...

Feb 4, 20261 min read

OpenLayers , Feature 찾는법

안녕하세요! 오늘은 개발 과정에서 겪었던 까다로운 버그와 그 해결 과정을 공유해볼까 합니다. OpenLayers를 사용한 지도 애플리케이션에서 발생한 문제로, 분명히 존재해야 할 피처(feature)를 getFeatureById로 찾지 못하는 상황이었습니다. 문제 발생: "ID도 있고, 레이어도 있는데 왜 못 찾니?" 문제는 otdr.js 파일의 highlightTracks라는 함수에서 시작되었습니다. 이 함수는 특정 트랙(track)들을 ...

Jan 28, 20263 min read
마

마리월드

13 posts