동기식 프로그래밍 스타일인 **연속 전달 스타일(CPS)**과 비동기 API를 일반적으로 사용하는 플랫폼으로의 적응은 쉽지 않을 수 있다. 비동기 코드는 구문이 실행되는 순서를 예측하기 어렵게 할 수 있다. 비동기 제어 흐름을 다루기 위해 콜백을 사용할 때 가장 흔히 하는 실수는 콜백 지옥에 빠지거나 코드가 간단한 루틴에서조차 가독성이 떨어지고 유지하기 힘들어져서 수직적이라기보다 수평적으로 커지는 일이다.

이 장에서 배우게 될 것들

비동기 프로그래밍의 어려움

클로저와 익명 함수의 in-place 정의는 개발자가 코드베이스 지점들을 옮겨 다니지 않고 원할한 프로그래밍을 하게 해준다. 이는 KISS(Keep It Simple, Stupid) 원칙과 완전하게 부합한다. 이는 코드가 매끄럽게 흘러가게 해주고 이를 짧은 시간에 정의할 수 있게 해준다. in-place 콜백을 만드는 것이 절대적으로 요구되는 것은 아니다. 비동기 프로그래밍과 관련된 문제보다는 규칙 문제가 더 중요하다.

간단한 웹 스파이더 만들기

/**
 * URL을 입력받아 URL의 내용을 로컬 파일로 다운 받기
 */
import fs from 'fs';
import path from 'path';
import superagent from 'superagent';
import mkdirp from 'mkdirp';
import { urlToFilename } from '../utils.mjs';

export function spider(url, cb) {
  const filename = urlToFilename(url);
  /**
   * 1. 파일 존재 여부 확인 후 해당 url에서 이미 다운로드 했는지 검사
   * error 타입이 ENOENT면 파일이 존재하지 않으므로 파일 생성에 문제가 없음
   */
  fs.access(filename, err => {
    if (err && err.code === 'ENOENT') {
      console.log(`Downloading ${url} into ${filename}`);
      //2. 파일을 찾을 수 없을 경우
      superagent.get(url).end((err, res) => {
        if (err) {
          cb(err);
        } else {
          //3. 파일이 저장도리 디렉터리가 있는지 확인
          mkdirp(path.dirname(filename), err => {
            if (err) {
              cb(err);
            } else {
              //4. HTTP응답의 내용을 파일 시스템에 쓴다.
              fs.writeFile(filename, res.text, err => {
                if (err) {
                  cb(err);
                } else {
                  cb(null, filename, true);
                }
              });
            }
          });
        }
      });
    } else {
      cb(null, filename, false);
    }
  });
}

콜백 지옥 예시….

콜백 지옥(Callback hell)

많은 클로저와 in-place 콜백 정의가 코드의 가독성을 떨어뜨리고 관리할 수 없는 덩어리로 만드는 상황을 콜백 지옥이라고 한다. Node.js와 JavaScrip에서 일반적으로 가장 잘 알려져 있는 안티 패턴 중 하나이다.

콜백 모범 사례와 제어 흐름 패턴

일련의 비동기 작업들의 흐름을 제어하려면 특정 패턴과 기법을 사용해야만 하는 상황이 있다. 특히 외부 라이브러리를 사용하지 않고 일반 JavaScript만 사용하는 경우에는 더욱 그렇다.

콜백 규칙

비동기 코드를 작성할 때 명심해야 할 첫 번째 규칙은 콜백을 정의할 때 in-place 함수의 정의를 남용하지 않는 것이다. 다음은 중첩수준을 낮게 유지하고 일반적으로도 코드 체계를 개선하는데 도움이 되는 몇 가지 기본 원칙이다.