동기, 비동기
frontend 개발자인 나는 늘 동기와 비동기를 가까이하고 일을 하고있다. REST API를 사용하기 때문에 해당 요청에 의한 처리를 하는 방식과 들어온 데이터에 따라 처리를 어떻게 하는가에 따라 달라지는 로직들에 예민하기 때문인데…
다시금 동기와 비동기를 생각해 볼 계기가 생겼다.
동기와 비동기의 가장 큰 차이는 뭘까~?
frontend 개발자로서, 데이터의 요청과 응답 순서는 개발 과정에서 종종 큰 차이를 만들어냅니다. 제가 사용하는 방식 중 하나를 소개하자면, 대시보드 개발에서 종종 데이터가 순서에 맞지 않게 들어와 꼬여버리는 상황을 겪곤 했습니다. 이를 해결하기 위해 어떤 방식으로 접근했는지에 대해 이야기해 보려고 합니다.
대시보드는 여러 데이터를 한 화면에 모아 보여주는 시스템입니다. 하지만 데이터를 불러올 때 요청과 응답이 순차적이지 않은 상황이 발생하면, 대시보드의 UI가 엉망이 되어 버립니다. 예를 들어, 데이터1이 먼저 와야 하는데 데이터2가 먼저 오거나, 데이터3이 중간에 튀어나오는 경우입니다.
이런 상황에서는 대시보드의 시각적 흐름이 깨져 사용자 경험에 큰 영향을 줄 수 있습니다. "그럼 이 문제를 어떻게 해결할까?"를 고민하면 답이 나옵니다.
제가 선택한 방법은 동기화입니다. 데이터가 들어오는 순서를 보장하고, 그 이후에 화면에 데이터를 그리도록 처리하는 방식입니다.
동기화를 적용한 방법:
- 비동기 작업을 순서대로 처리하도록 동기를 걸었습니다.
- 데이터가 모두 수신된 후 작업을 시작해 대시보드가 순서에 맞게 그려지도록 했습니다.
↓ 간단한 코드로 표현하자면 ↓
async function fetchAndRenderDashboard() { const data1 = await fetch('/api/data1'); // 첫 번째 데이터 요청 const data2 = await fetch('/api/data2'); // 두 번째 데이터 요청 renderDashboard([data1, data2]); // 데이터를 순서대로 렌더링 }
이 과정을 통해 단순히 기능을 구현하는 것을 넘어, 사용자의 경험을 고려한 개발이 중요하다는 점을 늘 깨닫고 있어 정리해보았습니다~
아! 그리고 개발하다가 좀 심하게 깨달았던 부분이있었다.. 한가지 풀어보면
function getDataSync() { const data = fetch('https://api.example.com/data'); // 요청 보냄 console.log(data); // 응답이 올 때까지 여기서 멈춤 }
버튼을 통해서 데이터를 불러오는 상황이 있었는데 응답이 올 때까지 아무것도 못하고 브라우저 멈춰버리는 경우가 있었다… 이게
fetch
로 데이터를 요청했는데, 응답이 올 때까지 UI가 멈춰버리는 상황이 생긴것…이 때 사용자는 아무 것도 못하고 로딩화면만 볼 수 밖에 없어버려… 그래서 이걸 비동기로 걸어서 해결했었는데
async function getDataAsync() { console.log("데이터 요청 중..."); // 요청 전에 다른 작업 가능 const data = await fetch('https://api.example.com/data'); // 요청 보냄 console.log(data); // 응답이 오면 처리 }
여기서 브라우저는 데이터를 기다리는 동안 다른 버튼 클릭이나 애니메이션 작업을 처리할 수 있게 되서 사용자의 경험으로 더 좋은 상황이되는거지..
그럼 backend에서 동기와 비동기는 어떻게 쓸까?
언제 동기를 사용할까?
비동기가 항상 좋은 건 아니야. 동기 처리가 더 적합한 경우도 있어:
- 순차적인 데이터 흐름이 필요한 작업:
- 예: 금융 거래 시스템에서 계좌 이체는 입금/출금이 정확히 순서대로 이루어져야 함
- 작업 간 의존성이 강한 경우:
- 예: A 데이터를 먼저 처리한 후에야 B 작업을 시작할 수 있을 때
언제 비동기를 사용할까?
- 네트워크 요청과 I/O가 많은 경우:
- DB 쿼리, 파일 처리, 외부 API 호출 등
- 병렬 처리가 가능한 독립 작업:
- 예: 여러 사용자의 요청을 동시에 처리해야 할 때
- 실시간 서비스:
- 채팅, 스트리밍, 알림 시스템
블록킹, 논블록킹
난 이 개념을 처음 들었을 때 진짜 엄청 헷갈렸다… 동기, 비동기랑 비슷한대~?
블로킹/논블로킹은 함수 호출의 동작 방식에 대한 이야기
- 블로킹은 호출한 함수가 완료될 때까지 기다리는 방식.
- 논블로킹은 호출한 함수가 즉시 반환하고, 완료는 나중에 처리.
동기/비동기는 작업 간의 제어 흐름에 대한 이야기
- 동기는 작업이 순서대로 진행되고, 완료될 때까지 다음 작업을 시작하지 않아.
- 비동기는 작업이 병렬적으로 실행되며, 완료 후에 결과를 처리.
이렇게 봐도 조금은 헷갈린다… 그럼? 둘은 종종 함께 사용되지만, 완전히 같은 개념은 아니다.
블로킹 동기
- 함수가 작업을 완료할 때까지 기다리고, 다른 작업은 중단한다.
- 예:
readFileSync
(파일 읽기가 끝날 때까지 코드 실행 중단).
논블로킹 동기
- 사실상 이런 경우는 드물어. 동기 작업이 논블로킹으로 작동하기 어렵다.
블로킹 비동기
- 이론적으로 가능하지만 일반적으로 사용되지 않는다.
논블로킹 비동기✨
(실제로 백엔드에서 논블로킹 비동기 방식을 많이 사용하는데, 이는 서버의 효율성을 극대화하고 여러 요청을 동시에 처리하는 데 매우 유용하다고 한다~?)
- 함수가 작업을 즉시 반환하고, 완료된 후 콜백이나 프로미스를 통해 결과를 처리한다.
- 예:
readFile
(파일 읽기 중 다른 작업을 수행)
진짜 이해 안가던게 이영상을 보고 진짜 바로 이해가 됫다…
Blocking vs Non-Blocking
어떤 직원이 있다.
이 직원은 서류를 작성해서 상사에게 전달해야한다. 이때 상사이름은 블록킹 상사다.
서류를 받은 블록킹 상사는 서류 다 볼때까지 기다려주세요. 라고 말한다.
그리고 서류를 다 검토한 후에 이제 가셔도 좋습니다 라고 말합니다. 이게 바로 블록킹이다.
또 이번엔 논블록킹 상사에게 서류를 전달해야한다.
논블록킹 상사는 서류를 전달받고 가서 일보세요~ 라고한다.
그럼 이때 직원은 기다리는 동안 다른일을 처리할 수 있게 된것이다.
이게 다른 주체가 작업할 때 자신의 제어권이 있는지 없는지로 볼 수 있다.
블로킹 동기
"기다려야 하고, 순서대로 처리해야 한다."
상황:
- 직원이 블로킹 상사에게 서류를 전달.
- 상사가 말함: "기다려주세요, 제가 서류를 검토할 때까지 아무 일도 하지 말고 여기 서 있어요."
- 직원은 다른 일을 하지 못하고 상사가 서류를 다 검토할 때까지 기다림.
특징:
- 제어권은 상사에게 있고, 직원은 기다려야 함.
- 서류 검토가 끝날 때까지 다른 일을 하지 못함.
- 순차적으로만 작업 진행.
function blockingSync() { const result = reviewDocument(); // 상사가 서류 검토 (블로킹) console.log(result); // 검토가 끝난 후 작업 }
논블로킹 동기
"기다릴 필요는 없지만, 순서대로 처리해야 한다."
상황:
- 직원이 논블로킹 상사에게 서류를 전달.
- 상사가 말함: "가서 다른 일을 하세요. 하지만 내가 서류 검토를 끝내기 전에는 새로운 일을 시작하지 마세요."
- 직원은 기다리지는 않지만, 상사가 서류 검토를 끝내기 전까지는 다른 작업을 시작하지 못함.
특징:
- 제어권은 상사에게 있지만, 직원은 서류를 기다리는 동안 다른 작업을 처리 가능.
- 순차적 작업만 허용되기 때문에 제한적.
function nonBlockingSync() { setTimeout(() => { console.log("서류 검토 완료"); // 상사가 검토 완료 후 }, 1000); console.log("다른 일을 하는 중"); // 검토를 기다리며 작업 }
블로킹 비동기
"순서는 상관없지만 기다려야 한다."
상황:
- 직원이 블로킹 상사에게 서류를 전달.
- 상사가 말함: "기다려주세요. 제가 서류 검토를 끝낼 때까지 다른 일을 시작하지 말아주세요."
- 직원은 상사가 끝낼 때까지 반드시 기다려야 하지만, 검토가 끝난 후에만 다른 작업을 시작할 수 있음.
특징:
- 제어권은 상사에게 있음.
- 작업 순서가 유연하지만, 직원은 반드시 상사가 끝낼 때까지 기다려야 함.
async function blockingAsync() { const result = await reviewDocument(); // 상사가 검토 끝날 때까지 대기 console.log(result); // 검토 끝난 후 작업 }
논블로킹 비동기
"기다릴 필요도 없고, 순서도 상관없다."
상황:
- 직원이 논블로킹 상사에게 서류를 전달.
- 상사가 말함: "가서 다른 일 하세요! 제가 검토 끝나면 알려드릴게요."
- 직원은 상사가 검토를 끝낼 때까지 기다릴 필요 없이 자신의 일을 계속 처리하고, 상사가 검토 완료를 알려주면 그때 작업을 이어감.
특징:
- 제어권은 직원에게 있음.
- 작업 순서가 유연하고, 병렬적으로 여러 작업이 진행 가능.
- 효율성이 가장 높음.
function nonBlockingAsync() { reviewDocument((result) => { console.log(result); // 검토가 끝난 후 처리 }); console.log("다른 일을 처리 중"); // 검토 기다리지 않고 다른 작업 }
정리표: 블로킹/논블로킹 & 동기/비동기
유형 | 제어권 | 작업 순서 | 특징 |
블로킹 동기 | 상사에게 있음 | 순차적 처리 | 작업이 끝날 때까지 기다려야 하며, 다른 작업 불가. |
논블로킹 동기 | 상사에게 있음 | 순차적 처리 | 기다릴 필요는 없지만, 순서대로만 처리 가능. |
블로킹 비동기 | 상사에게 있음 | 순서 무관 | 기다려야 하지만 순서에 구애받지 않음. |
논블로킹 비동기 | 직원에게 있음 | 순서 무관 | 기다릴 필요도 없고, 병렬 작업 가능. |
결론
- 블로킹/논블로킹은 제어권이 누구에게 있는지를 나타냄.
- 동기/비동기는 작업 간의 순서와 병렬 처리 여부를 나타냄.
- 논블로킹 비동기가 가장 효율적이지만, 모든 상황에서 적합한 것은 아니야. 상황에 맞게 조합을 선택하면 된다.
Share article