[TIL/React] 2023/11/13
서론 ✍️사람은 why에 대한 본인만의 대답이 없을 때 흔들린다. 다들 무엇(what)을 하겠노라고 쉽게 얘기한다. 어떻게(how) 하겠다는 말로 무엇(what)을 강화한다. 그런데 지칠 때면 왜(why) 해야 하는지에 의심을 품기 시작하고, 명확한 답이 없을 때 혹은
서론 ✍️
사람은 why에 대한 본인만의 대답이 없을 때 흔들린다. 다들 무엇(what)을 하겠노라고 쉽게 얘기한다. 어떻게(how) 하겠다는 말로 무엇(what)을 강화한다. 그런데 지칠 때면 왜(why) 해야 하는지에 의심을 품기 시작하고, 명확한 답이 없을 때 혹은 잊었을 때 포기한다. 그렇다. 왜(why)는 초심이다.
통신에 대해 학습해야겠다고 생각했다. 왜(why) 통신을 배워야 되는지부터 설명하기 위해 앞에서 무게를 잡았다. 시작하겠다.
왜 통신을 학습해야 할까? ✍️

개인마다 프로그래밍을 정의하는 방식은 다양하지만, 결국 하나의 애플리케이션은 액션, 계산, 데이터로 구성된다. 정육각 프로젝트를 진행하며 '내가 만든 데이터'로 액션, 계산 처리에 대해서는 찍먹을 해봤다. 문제는, 대부분의 경우 데이터를 서버에서 받아오고 목적에 맞게 파싱한다. ``서버와 대화할 필요가 생겼기 때문에`` 통신을 학습해야 되는 것이다. 잊지 말자.
동기(sync) 그리고 비동기(async) ✍️
동기(sync)와 비동기(async)에 대해서는 왜 학습해야 할까? '비효율'이라는 주제로부터 시작된다. 동기는 '요청과 결과가 동시에 O', 비동기는 '요청과 결과가 동시에 X'이다. 배달 로직이 있다고 가정하자.
동기적으로 동작하는 배달 로직에서는 배달원이 짜장면을 배달한 뒤, 손님이 음식을 다 먹고 그릇을 내놓기까지 기다려야 한다. 요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 하는 것이 바로 동기(sync)이다.
그렇다면 비동기는 쉽게 이해할 수 있다. 요청과 결과가 동시에 발생하지 않는 것이 비동기(async)이다. 따라서 배달원은 다른 주문에 대한 배달을 수행할 수 있게 된다.
서버가 클라이언트로 데이터를 전송할 때까지 아무것도 할 수 없는 상태는 그 자체로 비효율적이다. 그렇기에 비동기적인 처리가 코드 내에서 이루어지게 된다. ``동기의 비효율, 그리고 그 비효율로부터 파생되는 비동기에 대한 논의, 이 부분이 통신의 시작이기 때문에`` 동기(sync)와 비동기(async)의 개념을 학습해야 하는 것이다.
콜백 지옥 개념의 등장 ✍️

동기의 비효율이 비동기 코드의 사용을 촉발했다고 이해했다. 문제는, 어쨌든 순서는 있어야 된다는 것이다. 비동기 작업에 대한 여러 함수가 있어도, 작업간에 의존성이 있는 경우에는 당연히 순서를 부여해야 한다. 이때 비동기에 대한 코드는 콜백 함수로 표현된다. ``콜백 함수에 콜백 함수가 들어가면서 코드의 depth가 깊어지는 현상``, 즉 콜백 지옥의 배경은 바로 이렇게 등장하게 된다. 코드로 표현하면 다음과 같다.
// 주문 받기
const takeOrder = (customer, callback) => {
setTimeout(() => {
console.log(`주문 받음: ${customer}`);
callback();
}, 1000);
};
// 요리 시작
const startCooking = (callback) => {
setTimeout(() => {
console.log("요리 시작");
callback();
}, 1000);
};
// 배달 준비
const prepareDelivery = (callback) => {
setTimeout(() => {
console.log("배달 준비 완료");
callback();
}, 1000);
};
// 배달 진행
const doDelivery = (address, callback) => {
setTimeout(() => {
console.log(`배달 중: ${address}`);
callback();
}, 1000);
};
// 주문에서 배달까지의 로직
takeOrder("손님 1", function () {
startCooking(function () {
prepareDelivery(function () {
doDelivery("손님 1 집주소", function () {
console.log("배달 완료");
});
});
});
});
Promise 개념의 등장 ✍️
따라서 ``Promise는 콜백 지옥에 대한 대안``이다. Promise는 객체이다. 코드를 먼저 살펴보자.
// 주문 받기
function takeOrder(customer) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`주문 받음: ${customer}`);
resolve();
}, 1000);
});
}
// 요리 시작
function startCooking() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("요리 시작");
resolve();
}, 1000);
});
}
// 배달 준비
function prepareDelivery() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("배달 준비 완료");
resolve();
}, 1000);
});
}
// 배달 진행
function doDelivery(address) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`배달 중: ${address}`);
resolve();
}, 1000);
});
}
// 주문 및 배달 시뮬레이션
takeOrder("고객 A")
.then(function () {
return startCooking();
})
.then(function () {
return prepareDelivery();
})
.then(function () {
return doDelivery("고객 A의 집");
})
.then(function () {
console.log("배달 완료");
});Promise는 비동기 작업의 완료 또는 실패를 처리하기 위한 하나의 매커니즘이다. Promise 객체가 제공하는 then 메서드를 활용하면, then 메서드 내부에 Promise가 이행되었을 때 실행되는 콜백을 등록할 수 있게 된다. 콜백 지옥 커리를 타지 않아도 순서가 보장되는 것이 Promise의 최대 장점이다.
콜백 지옥으로 작성한 코드와 결과는 동일하지만, ``코드가 좀 더 선형적이고 읽기 쉬워진 형태로 변경되었다는 것을 확인할 수 있다.``
async/await ✍️
async/await는 한마디로 ``Promise의 Syntactic sugar, 즉 더 쉬운 버전``이다. 어떻게든 더 직관적으로 사용하고 싶어하는 개발자들의 열망이 낳은 산물인 것이다. 코드로 얘기하자.
// 주문 받기
function takeOrder(customer) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`주문 받음: ${customer}`);
resolve();
}, 1000);
});
}
// 요리 시작
function startCooking() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("요리 시작");
resolve();
}, 1000);
});
}
// 배달 준비
function prepareDelivery() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("배달 준비 완료");
resolve();
}, 1000);
});
}
// 배달 진행
function doDelivery(address) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`배달 중: ${address}`);
resolve();
}, 1000);
});
}
// 주문 및 배달 시뮬레이션
async function simulateDelivery() {
await takeOrder("고객 A");
await startCooking();
await prepareDelivery();
await doDelivery("고객 A의 집");
console.log("배달 완료");
}
// 실행
simulateDelivery();async/await를 사용하면, ``비동기 작업이 마치 '동기 코드'처럼 보이게 된다.`` 각 함수 앞에 await 키워드를 붙여 해당 작업이 완료될 때까지 기다리고, 그 후 다음 작업을 수행한다. 이로써 코드가 훨씬 더 읽기 쉽고 관리하기 쉬워지는 장점이 있다.
try/catch ✍️
이 정도 했으면 된거 아니야? 싶지만, 아직 try/catch가 남았다. try/catch의 존재 이유를 코드를 통해 살펴보자.
// 주문 받기
function takeOrder(customer) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
try {
if (customer === "고객 B") {
throw new Error("주문 오류: 해당 고객에게는 배달하지 않습니다.");
}
console.log(`주문 받음: ${customer}`);
resolve();
} catch (error) {
reject(error);
}
}, 1000);
});
}
// 요리 시작
function startCooking() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
try {
// 요리 중 예외 상황
throw new Error("요리 중 오류 발생");
console.log("요리 시작");
resolve();
} catch (error) {
reject(error);
}
}, 1000);
});
}
// 배달 준비
function prepareDelivery() {
return new Promise(function (resolve) {
setTimeout(function () {
console.log("배달 준비 완료");
resolve();
}, 1000);
});
}
// 배달 진행
function doDelivery(address) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log(`배달 중: ${address}`);
resolve();
}, 1000);
});
}
// 주문 및 배달 시뮬레이션
async function simulateDelivery() {
try {
await takeOrder("고객 A");
await startCooking();
await prepareDelivery();
await doDelivery("고객 A의 집");
console.log("배달 완료");
} catch (error) {
console.error(`에러 발생: ${error.message}`);
}
}
// 실행
simulateDelivery();try/catch 블록을 사용하는 이유는 비동기 코드에서 발생할 수 있는 예외(에러)를 처리하고, 프로그램의 안정성을 높이기 위함이다. 비동기 작업 중에 에러가 발생하면 해당 예외는 프로미스 객체의 reject 메서드를 통해 전파된다. 이때, async/await를 사용하면 코드가 '동기적으로 작성'되었지만 실제로는 '비동기적으로 동작'하므로 '예외 처리'가 중요하다.
More to read
프론트엔드와 백엔드 사이
HTTP 상태 코드는 프론트엔드에서 백엔드로 보냈던 요청의 수행 결과를 의미하는 일종의 약속이며, API를 구성하는 핵심 요소 중 하나입니다. 상태 코드와 관련하여, 백엔드는 잘 모르는 프론트엔드의 슬픈 사정이 있습니다.아래는 요청이 실패했음에도, 백엔드에서 상태 코드
JWT토큰 관리 방식 톺아보기
0. 들어가며 🎯 서비스에 접근하려는 사용자가 누구인지 확인하는 과정을 사용자 인증이라고 합니다. 인증된 사용자에게 주어진 권한을 확인하는 작업은 인가라고 부릅니다. 이번 글에서는 인가는 다루지 않습니다. 사용자 인증에는 많은 방식이 있지만, 오늘은 세션 인증 방
A2AA2A / MCP 멀티 에이전트 오케스트레이션
0. 들어가며 ✍️ Google for Developers에, 레스토랑 공급망 시나리오로 엮은 6대 프로토콜(MCP, A2A, UCP, AP2, A2UI, AG-UI)에 대한 가이드가 게시된 이후, MCP와 A2A부터 구현해 보는 것이 좋을 것 같다는 생각이 들었습니