AI 웹 개발 교실

선택 · 실행과 동시성 용어 지도

콜백·리스너·동기·비동기를 구분하고, 대리자·코루틴·스레드는 궁금할 때 찾아봅니다.

이 단원의 실습 파일 25 KB ZIP 다운로드

02 이벤트·비동기 실습 · 03-0 소켓 채팅으로 돌아가기 · 02 실습 ZIP

궁금할 때 찾아보는 선택 참고 자료. 채팅을 하다가 함수나 실행 순서가 궁금해지면 필요한 부분만 펼쳐 본다. 읽지 않아도 기본 실습의 진도와 완료 여부에는 영향이 없다. 02-4의 이벤트·Promise·await와 연결해 볼 수 있고, 더 넓은 용어는 해당 문제가 생길 때 찾아보면 된다.

궁금할 때 먼저 볼 세 가지

  1. fn은 함수 값, fn()은 지금 호출. other(fn)은 다른 코드에 함수를 넘긴다. other(fn())은 먼저 fn()을 실행하고 그 반환값을 넘긴다.
  2. 콜백·리스너·핸들러는 함수의 역할을 말한다. 같은 함수가 세 역할에 해당할 수 있다. 이름보다 “누가 언제 호출하는가?”를 먼저 찾는다.
  3. 등록과 실행은 다르며, 콜백이 항상 비동기인 것은 아니다. on에 함수를 넘기는 줄과 나중에 그 함수의 본문이 실행되는 순간을 나누어 본다. EventEmitter의 emit은 등록된 리스너를 동기적으로 호출한다.

예를 들어 socket.on('data', handleData)에서 handleData는 on에 전달한 콜백이고, data 이벤트에 등록한 리스너이며, 데이터를 처리하는 핸들러다. 세 종류의 별개 함수를 만들어야 한다는 뜻이 아니다. 콜백 정의, Node EventEmitter

한눈에 보기: 서로 어떤 질문에 답하는가?

질문관련 용어지금 연결할 장면
함수를 누가 호출하는가?콜백, 리스너, 핸들러socket.on('data', fn)
호출과 다음 코드가 어떤 순서로 이어지는가?동기, 비동기직접 호출과 타이머 비교
기다리는 동안 이 실행 흐름이 다른 일을 할 수 있는가?블로킹, 논블로킹파일·네트워크 작업을 기다림
완료 또는 실패를 어떻게 이어 받는가?Promise, await파일 저장 후 Saved 출력
멈춘 지점에서 어떻게 실행을 이어 가는가?코루틴다른 언어·런타임에서 다시 만날 때
프로그램의 자원과 실행 흐름은 어떻게 나뉘는가?프로세스, 스레드서버와 클라이언트를 따로 실행
여러 일이 함께 진행되는가, 실제 동시에 실행되는가?동시성, 병렬성여러 학생의 연결 처리
공유하는 값과 실행 순서를 어떻게 맞추는가?동기화, 락, 뮤텍스, 세마포어이후 여러 요청이 같은 데이터를 변경

이 표의 단어를 한 줄의 진화 순서로 외우지 않는다. 예를 들어 콜백을 Promise로 바꾸었다고 스레드가 늘어나거나 병렬 실행이 되는 것은 아니다.

선택 A. 함수를 넘기고, 등록하고, 실행하기

콜백 / 리스너 / 핸들러

말이 수업에서의 뜻혼동하기 쉬운 점
callback, 콜백다른 코드가 호출하도록 인자로 넘긴 함수받는 코드가 즉시 호출할 수도, 나중에 호출할 수도 있다.
listener, 리스너특정 이벤트를 받도록 등록한 함수등록한 순간에 본문이 실행되는 것은 아니다.
handler, 핸들러어떤 요청·이벤트·상황을 처리하는 역할특정 언어의 별도 함수 문법을 뜻하지 않는다.

라이브러리마다 listener와 handler를 부르는 방식은 조금씩 다르다. 여기서는 Node의 on에 등록하는 함수를 리스너라고 부른다. Node 이벤트 API

동기 / 비동기 / 블로킹

  • 동기 호출: 호출한 함수의 실행이 끝나 돌아온 뒤 호출부의 다음 코드로 이어진다. formatNote(note)와 bell.emit('ring') 안에서 실행되는 리스너를 떠올린다.
  • 비동기 작업: 작업의 시작과 완료 처리가 분리된다. 타이머를 설정한 코드가 먼저 계속되고, 이후 콜백이 실행될 수 있다. 소켓의 데이터 도착도 처음 등록한 코드의 실행과 별도로 일어난다.
  • 블로킹 / 논블로킹: 무엇이 기다리며 막히는지를 함께 말해야 한다. Node의 readFileSync는 파일 읽기가 끝날 때까지 그 JavaScript 실행 흐름을 막는다. 비동기 파일 읽기는 기다리는 동안 다른 JavaScript 콜백을 실행할 기회를 준다. Node의 블로킹과 논블로킹 설명

이벤트 이름만 보고 실행 시점을 판단할 수 없다. bell.emit('ring')은 리스너 호출을 마친 뒤 다음 줄로 넘어간다. 소켓은 나중에 데이터가 준비되어 이벤트를 발생시키지만, 이벤트가 발생한 뒤 각 리스너를 호출하는 동작은 EventEmitter의 규칙을 따른다. Node의 동기·비동기 리스너 설명

소켓 리스너 하나가 긴 계산을 계속하면 같은 JavaScript 스레드의 다른 학생 응답도 늦어질 수 있다. 콜백을 쓴다는 사실이 긴 계산을 자동으로 나누거나 새 스레드를 만들어 주지는 않는다. Node 이벤트 루프를 막지 않기

선택 B. 완료를 표현하는 도구와 언어별 기능

Promise / async / await

Promise는 작업이 아직 진행 중인지, 완료되었는지, 실패했는지를 표현하는 객체다. async 함수는 Promise를 반환한다. await는 그 결과에 맞춰 현재 함수의 뒤쪽 코드를 이어 가거나 실패를 전달한다. 이 수업의 .mjs처럼 모듈의 최상위에서도 await를 쓸 수 있다.

await는 전체 Node 프로세스를 멈추거나 작업마다 새 스레드를 만드는 명령이 아니다. await를 붙여도 동기적인 긴 계산은 자동으로 나뉘지 않는다. 02-4의 order.mjs에서 await work를 work로 바꾸면 결과 대신 Promise가 보이는 실험으로 다시 확인한다. Promise, await

delegate, 대리자 — C#에서 만났을 때

C#의 delegate는 정해진 매개변수와 반환 타입에 맞는 메서드를 참조하는 타입이다. 그 참조를 전달하거나 호출하여 콜백·이벤트 처리를 구성할 수 있다. “콜백”은 맡긴 역할이고, “C# delegate”는 그 역할을 구현할 때 사용할 수 있는 언어 기능이다. JavaScript 함수에 붙이는 또 다른 필수 이름은 아니다. C# delegate 공식 설명

coroutine, 코루틴

핵심은 실행 상태를 유지한 채 중단하고, 멈춘 지점에서 다시 이어 갈 수 있는 계산이다. 어느 스레드에서 실행하는지, 무엇이 재개시키는지는 언어와 런타임에 따라 다르다.

예를 들어 Kotlin/JVM에서는 여러 코루틴이 스레드를 공유할 수 있고, 사용하는 dispatcher에 따라 다른 스레드에서 재개할 수도 있다. 코루틴 하나와 OS 스레드 하나가 같은 것은 아니다. JavaScript의 async/await와 중단·재개라는 관점을 연결할 수 있지만, Kotlin 등의 코루틴 기능 전체와 같다고 설명하지 않는다. Kotlin 코루틴

선택 C. 여러 일이 겹칠 때

프로세스 / 스레드 / 이벤트 루프

  • 프로세스: 운영체제가 실행 중인 프로그램의 메모리와 자원 등을 관리하는 단위. 이 수업에서 서버와 클라이언트를 각각 node로 실행하면 별도 프로세스다.
  • 스레드: 프로세스 안에서 실행을 진행하는 흐름. 같은 프로세스의 스레드들은 메모리 등 자원을 공유할 수 있다. 운영체제의 프로세스와 스레드 설명
  • 이벤트 루프: Node가 준비된 작업과 콜백을 이어 처리하는 구조. 지금의 소켓 예제는 한 JavaScript 스레드에서 리스너들을 실행한다. Node 내부나 운영체제 전체에 스레드가 하나뿐이라는 뜻은 아니다. Node 이벤트 루프

03-0에서는 이벤트 루프의 세부 단계나 큐 이름을 외울 필요가 없다. “한 함수가 오래 걸리면 다른 연결에 어떤 영향을 줄까?”는 궁금할 때 더 살펴볼 질문이다.

동시성 / 병렬성

**동시성(concurrency)**은 여러 작업이 겹치는 기간 동안 진행되도록 구성하는 것, **병렬성(parallelism)**은 여러 작업이 실제로 동시에 실행되는 것이다. 한 실행 흐름이 기다리는 작업 사이를 번갈아 처리해도 동시성은 가능하다. Go 팀의 동시성과 병렬성 설명

여러 학생의 채팅 연결을 유지한다고 학생마다 JavaScript 스레드가 하나씩 생기는 것은 아니다. 비동기 API를 썼다는 이유만으로 여러 CPU 코어에서 우리 함수가 병렬 실행된다고 결론 내릴 수도 없다.

동기화 / 락 / 뮤텍스 / 세마포어

여기서 **동기화(synchronization)**는 여러 실행 흐름이 공유 자원에 접근하는 방식이나 실행 순서를 맞추는 일이다. **동기 호출(synchronous call)**과 서로 다른 질문에 답한다. 이 문제는 이후 데이터 수업에서 “두 요청이 같은 값을 읽고 바꾸면?”이라는 장면으로 다시 다룬다.

용어핵심 역할간단한 이미지
임계 구역공유 자원을 안전하게 다루기 위해 조정이 필요한 코드 구간함께 고치는 점수판
락(lock)접근을 조정하는 잠금 장치·방법을 통칭들어가기 전에 잠그고 나올 때 풀기
뮤텍스(mutex)한 번에 하나의 소유자만 보호된 구간에 접근하게 함열쇠 하나
세마포어(semaphore)허용 수를 세어 자원 접근이나 진행을 조정함남은 입장권 수

실제 소유권·대기·해제 규칙은 API마다 확인한다. 개수 1인 세마포어와 뮤텍스도 모든 규칙이 같지는 않다. Windows 동기화 객체의 뮤텍스·세마포어 비교

이 수업의 작은 채팅 서버에 곧바로 락을 추가할 필요는 없다. 반대로 JavaScript를 한 스레드에서 실행한다고 모든 데이터 충돌이 사라지는 것도 아니다. 예를 들어 요청 A와 B가 각각 값을 읽은 뒤 await로 실행을 양보하고 나중에 값을 덮어쓰는 상황은 별도로 검토해야 한다.

선택 D. 이름의 뜻과 역사 구분하기

아래는 기억을 돕는 영어 뜻풀이다. 누가 언제 처음 이 용어를 만들었는지 증명하는 어원 설명은 아니다.

이름뜻을 붙여 읽는 방법이름만으로 결론 내리면 안 되는 것
callback넘긴 함수를 받는 쪽에서 호출해 줌반드시 나중에 호출한다.
listener / handler듣는 역할 / 처리하는 역할서로 다른 종류의 함수 문법이다.
delegate역할을 맡긴 대리자모든 언어에서 C#과 같은 기능이다.
coroutine함께 이어 가는 routine으로 기억여러 코어에서 반드시 동시에 돈다.
thread실행이 이어지는 실·가닥으로 기억콜백마다 스레드가 생긴다.
lock / semaphore자물쇠 / 신호 장치로 기억API별 동작도 일상 물건과 똑같다.

확인할 수 있는 이름의 근거도 따로 둔다. Microsoft 문서는 mutex를 mutually exclusive access, 즉 상호 배제 접근과 연결해 설명한다. 뮤텍스 이름과 역할

역사 자료로는 Melvin E. Conway의 1963년 논문에서 coroutine이라는 용어와 서로 제어를 넘기며 중단 지점에서 이어 가는 구성을 확인할 수 있다. 이 문서만으로 “최초로 만든 사람·연도”까지 단정하지 않는다. Design of a Separable Transition-Diagram Compiler, 1963, 396–398쪽

용어를 만날 때는 언어·라이브러리 → 실제 호출하는 줄 → 실행 시점 → 공유하는 자원 순서로 확인한다. 모르는 이름이 늘어날 때도 이 네 질문을 코드에 대입하면 된다.

이 페이지에서