빠른 흐름
javascript
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
const rl = readline.createInterface({ input, output });
try {
const name = await rl.question("이름을 입력하세요: ");
const confirm = await rl.question(`"${name}"으로 저장할까요? (y/n) `);
if (confirm.toLowerCase() === "y") {
await saveUser(name);
console.log("저장 완료");
}
} finally {
rl.close();
}입력 흐름
먼저 머릿속에 들어와야 하는 기본형은 아래입니다.
- 대화형 프롬프트:
readline/promises - 순차 질문:
await rl.question(...) - 파이프 입력 처리:
for await (const line of rl) - 종료 보장:
try/finally+rl.close() - TTY 여부에 따라 대화형/비대화형 분기
사용자와 대화하면 question()
설정 마법사, 확인 프롬프트, 간단한 입력 수집은 Promise 기반 question()이 가장 읽기 쉽습니다.
stdin 파이프 처리면 for await...of
cat data.txt | node tool.js 같은 흐름은 인터랙션이 아니라 스트림 처리입니다. 이때는 createInterface({ input: process.stdin })와 비동기 순회가 더 맞습니다.
종료 조건은 rl.close()
대화형 모드에서 작업이 끝났는데 프로세스가 살아 있으면 보통 이 부분입니다. try/finally로 닫는 습관이 중요합니다.
대화형인지 파이프 입력인지 먼저 나눈다
같은 CLI라도 터미널에서 직접 실행할 때와 cat file | tool처럼 파이프로 들어올 때 기대 UX가 다릅니다.
프롬프트를 띄워야 하는지, stdin을 그냥 소모해야 하는지 process.stdin.isTTY 기준으로 먼저 나누는 편이 좋습니다.
언제 readline을 쓸까
체크포인트
- 대화형 프롬프트:
node:readline/promises - 순차 질문:
await rl.question(...) - 파이프 입력 처리:
for await (const line of rl) - 종료 보장:
try/finally+rl.close() - TTY 여부에 따라 대화형/비대화형 흐름을 나눈다
주의할 점
대화형 CLI에서 rl.close()를 빼먹으면 작업은 끝났는데 프로세스가 안 꺼진다. 이 카드에서 가장 자주 다시 찾는 포인트도 여기다.
참고 링크
1 sources