울음참고 개발공부
article thumbnail
728x90

 

 

 

 


 

 

 

해당 포스트는 AJAX 기술에 대해 학습하던 중에 아래 개념을 더 확실하게 하기 위해 작성했다

 

-> 동기/ 비동기에 대한 확실한 이해

-> 비동기호출의 콜백 지옥 예시 

 

 

 

2025.04.08 - [Frontend/JavaScript] - Fetch API 란 ? AJAX 기술 이해하기

 

 

 


 

 

 

# 동기와 비동기 개념 

 

구분  개념
동기(Synchronous) 어떤 작업을 끝날 때까지 작업 안하고 기다림
비동기(Asynchronous) 어떤 작업이 끝날 때까지 기다리지 않고 바로 실행함 

 

 

 

동기 호출 

 

console.log('1');

const res = $.ajax({
  url: 'https://...',
  async: false
});

console.log('2'); // 요청 끝날 때까지 아예 안 찍힘

 

 

 

동기호출은 응답을 보내고 응답이 올때까지 기다렸다가 다음수행할 것들을 처리하는데, 몇가지 문제점이 있기 때문에 거의 사용하지 않는다. 

 

 

동기 호출을 쓰지않는 이유

 

1. 브라우저 멈춤 (  UI 프리징 )

- 요청하고 응답을 기다리는 동안 클릭도 안되고 페이지 전체가 얼어버리기 때문에 UX(사용자 경험) 최악

 

2. 메인 스레드 블로킹

- 자바스크립트는 싱글 스레드인데 동기 호출을 하는 순간 메인 스레드를 막아버려서 그 사이에 렌더링이나 이벤트 처리를 막아버린다. ( 그 때문에 브라우저 멈춤 현상 발생 ) 

 

3. 성능 저하 및 예측 어려움

- 페이지가 뭠췄을 때 느린 네트워크 환경이라면 더더욱 브라우저가 느리게 실행된다

- 동기 호출을 사용하면 코드 실행 순서를 명확히 제어하기 어렵고, 디버깅도 헷갈릴 수 있다.

 

 

 

동기호출 옵션은 XMLHttpRequest 에서만 지원한다 

fetch()나 axios는 Promise 기반임
Promise는 원래 비동기 설계 철학 위에서 돌아감
그래서 async: false 같은 옵션 자체가 없음! ( 해도 무시됨 )

 

 

 

 

비동기 호출 

 

# 예시 1) 

<script>
  console.log('1. 요청 시작');

  $.ajax({
    url: 'https://jsonplaceholder.typicode.com/posts/1',
    method: 'GET',
    success: function(data) {
      console.log('3. 응답 받음:', data);
    }
  });

  console.log('2. 요청 이후 코드 실행');
</script>

 

 

실행 순서 

1. 요청 시작
2. 요청 이후 코드 실행
3. 응답 받음: {...}

 

 

다음과 같이 비동기 호출을 하게 되면 요청후 응답을 기다리지않고 바로 다음 코드를 실행한 후 콜백이 오면 그에 대해 응답한다.

이건 어떤 상황에서 문제가 있을까 ? 

 

# 예시 ) 

function getUser() {
	$.ajax({
    	url:'https://jsonplaceholder.typicode.com/users/1',
        method: 'GET',
        success: function(user) {
        	console.log('유저 정보 가져오기 성공 : ', user);
        },
        error: function() {
        	console.error('요청 실패');
        }
    });
}

function doSomthingUsingUserInfo() {
	console.log('가져온 유저 정보로 처리할 작업');
}

getUser(); 
doSomthingUsingUserInfo();

 

 

코드 실행 순서로만 보면 실행 순서가 getUser() -> doSomthingUsingUserInfo() 로 정상적으로 이루어질 것 같다. 

 

예상 콘솔 로그 ) 유저 정보 가져오기 성공 ~ -> 가져온 유저 정보로 처리할 작업 

실제 콘솔 로그 ) 가져온 유저 정보로 처리할 작업 -> 유저 정보 가져오기 성공 ~ 

 

이는 비동기 요청의 특징 때문이다 . 

 

 

비동기호출을 순서대로 실행하기 위해 Promise 객체를 활용하여 순서대로 시행되는 것처럼 할 수 있다. 

 

function getUserPromise() {
	return new Promise((resolve, reject) => {
    	$.ajax({
        	url: 'https://jsonplaceholder.typicode.com/users/1',
            methode: 'GET',
            success: resolve,
            error: reject
        });
    });
}

 function doSomthingUsingUserInfo() {
 	console.log('가져온 유저 정보로 처리할 작업');
 }

getUserPromise()
	.then(uesr => {
    	console.log('유저: ',user);
        console.log('이름: ',user.name);
        doSomthingUsingUserInfo();
    })
    .catch(err => {
    	console.error('에러: ', err);
    });

 

 

 

이렇게 순서를 보장하고 싶으면 .then() 안에 순서대로 적어줘야 한다. 

 

비동기 호출이 순서대로 실행되면 그게 동기호출 아닌가.. ?

=> 아니다! 순서대로 실행되는 것 처럼 보이게 할 뿐이다 !

# async/await 을 사용한 비동기 호출 예시 
async function fetchData() {
  console.log('1');
  const res = await fetch('https://...');
  const data = await res.json();
  console.log('2');
}

fetchData();
console.log('3'); // 먼저 실행됨!


자바스크립트는 싱글 스레드 언어기 때문에 한 번에 하나의 작업만 실행한다.
await 는 기다리지만, 전체 앱은 멈추지 않은 상태에서 기다리는 것이다.

반면, 동기 호출시에는 실제로 앱이 멈춘상태에서 진행된다. 

 

 

 

 

비동기 호출의 콜백 지옥

  • 비동기 + 중첩 콜백의 조합에서 생기는 문제
  • 콜백 안에 콜백을 넣는 구조 (중첩이 계속 반복됨) 

 

# 예시 

<script>
    $.ajax({
        url: 'https://jsonplaceholder.typicode.com/users/1',
        method: 'GET',
        success: function (user) {
            console.log('1. 유저 정보:', user);

            $.ajax({
                url: 'https://jsonplaceholder.typicode.com/posts?userId=' + user.id,
                method: 'GET',
                success: function (posts) {
                    console.log('2. 글 목록:', posts);

                    $.ajax({
                        url: 'https://jsonplaceholder.typicode.com/comments?postId=' + posts[0].id,
                        method: 'GET',
                        success: function (comments) {
                            console.log('3. 첫 글 댓글:', comments);
                        },
                        error: function () {
                            console.error('댓글 가져오기 실패');
                        }
                    });

                },
                error: function () {
                    console.error('글 목록 가져오기 실패');
                }
            });

        },
        error: function () {
            console.error('유저 정보 가져오기 실패');
        }
    });
</script>

 

 

 

 

예시에서 단계 1에서 success 콜백시에 단계2를 수행하는데, 단계 2에서는 단계 3의 콜백을 받고 있다. ( 중첩 계속 반복 ) 

 

 

 

# 문제점 

 

  • 들여쓰기 헬
  • 각각의 error도 따로 있어서 복잡도 상승 
  • 위의 이유로 로직 수정/유지보수 어려움 ( 콜백 지옥이라고 부르는 이유 ) 
  •  Promise 가 없을 때, XHR 이나 $.ajax() 같은 비동기 작업을 순차적으로 처리하는 과정에서 발생했음 

 

XMLHttpRequest 사용하는 것이 콜백지옥의 원인? 
-> ❌

=> XMLHttpRequest 을 중첩으로 사용해서 발생하는 것 !

 

 

 

# 해결 

 

1. Promise 버전으로 리팩토링 하기 

function getUser(userId) {
    return $.ajax({
        url: 'https://jsonplaceholder.typicode.com/users/' + userId,
        method: 'GET'
    });
}

function getPost(userId) {
    return $.ajax({
        url: 'https://jsonplaceholder.typicode.com/posts?userId=' + userId,
        method: 'GET'
    });
}

function getComment(postId) {
    return $.ajax({
        url: 'https://jsonplaceholder.typicode.com/comments?postId=' + postId,
        method: 'GET'
    });
}

// 체이닝
getUser(1)
    .then(user => {
        console.log('1. 유저 정보:', user);
        return getPost(user.id);
    })
    .then(posts => {
        console.log('2. 글 목록:', posts);
        return getComment(posts[0].id);
    })
    .then(comments => {
        console.log('3. 첫 글 댓글:', comments);
    })
    .catch(err => {
        console.error('에러 발생:', err);
    });

 

체이닝(Chaining)이란?

함수를 호출한 결과로 또 다른 함수를 계속 이어붙이는 방식임.
특히 Promise에서 .then()을 계속 이어붙이는 걸 체이닝이라고 많이 말함

 

 

 

2. async/await 버전 

 

코드가 더 간결하고 요즘 방식임 

async function loadUserData() {
    try {
        const user = await getUser(1);
        console.log('1. 유저 정보:', user);

        const posts = await getPost(user.id);
        console.log('2. 글 목록:', posts);

        const comments = await getComment(posts[0].id);
        console.log('3. 첫 글 댓글:', comments);

    } catch (err) {
        console.error('에러 발생:', err);
    }
}

loadUserData();

 

 

 

 


 

 

그동안 주로 jQuery 로 프로젝트를 진행하며 $.ajax() 에만 익숙해졌었는데 이번에 Raect,Vue 를 공부하며 Fetch() API  사용에 익숙해지기 위해 이것 저것 정리를 해봤다. 

 

나도 이제 요즘 개발자 되야지 .. ㅋ.ㅋ. ~~~ 

 

 


 

728x90

'Frontend > JavaScript' 카테고리의 다른 글

Fetch API 란 ? AJAX 기술 이해하기  (0) 2025.04.08
attr() 과 prop() 의 차이  (0) 2024.03.18
profile

울음참고 개발공부

@메각이

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!