작업할 때마다 이 명령으로 타입을 확인해왔습니다.
npx tsc --noEmit | grep "error TS"
아무것도 안 나오면 통과라고 읽었고, 한 세션에서 여러 번 그렇게 읽고는 커밋 메시지에도 그대로 적었습니다. 그게 전부 거짓이었습니다.
필터를 빼자 타입스크립트 에러가 6개 나왔습니다
의심하게 된 계기는 엉뚱한 데 있었습니다. 타입스크립트가 아니라 화면이 먼저 이상했는데, 분명히 넣은 코드가 안 나와서 파일을 다시 열어보니 그 자리에 아무것도 없었습니다.
그래서 이번엔 필터를 빼고 돌려봤습니다.
npx tsc --noEmit -p . --pretty false
그동안 한 번도 안 보이던 에러가 6개 나왔습니다.
grep 이 못 잡은 이유는 기본 출력이 사람 눈으로 읽으라고 꾸며진 형태이기 때문입니다.
색과 줄바꿈이 끼어들면서 제가 찾던 error TS 라는 연속된 문자열이 그 형태로는 나오지 않았던 거죠.
--pretty false 를 주면 기계가 읽는 한 줄짜리 형식으로 바뀌고, 그때는 같은 grep 도 잘 잡습니다.
실패와 침묵이 같은 모양이었습니다
문제는 grep 문법이 아니라 이 방식의 구조 자체에 있었습니다.
통과했을 때도 출력이 없고, 검사가 아예 안 걸렸을 때도 출력이 없으니, 두 상태가 화면에서는 완전히 같아 보입니다. 그러니까 저는 매번 통과를 본 게 아니라 아무 말 없음을 보고 통과라고 해석해온 셈이고, 그 해석이 틀릴 수 있다는 신호는 어디에도 없었습니다.
사실 같은 저장소에서 캐시가 적중했다고 말하면서 아무것도 복원하지 않던 빌드 도구를 겪은 적이 있습니다. 그때도 도구는 성공을 보고했지만 실제로는 아무 일도 하지 않았습니다. 성공 메시지와 성공은 다른 것이라는 걸 두 번째로 배운 셈입니다.
하나는 다섯 번의 배포를 타고 나갔습니다
6개 중 하나는 타입 하나가 엉뚱한 자리에 들어간 것이었습니다. 필드를 어떤 타입에 추가했어야 하는데, 같은 이름의 문자열이 파일 안에 두 군데 있다 보니 함수 파라미터 쪽에 붙어 있었습니다.
그 상태로 커밋된 뒤 무선 업데이트로 다섯 번이나 나갔습니다.
동작에는 영향이 없었습니다. 번들러가 타입을 지우고 내보내기 때문에 런타임에는 그 선언이 아예 존재하지 않고, 그래서 사용자가 겪은 문제도 없었습니다.
문제는 다른 데 있습니다. 저는 그 다섯 번 동안 검사를 통과했다고 보고했고 그 보고가 사실이 아니었으니, 그 사이에 진짜 타입 에러가 났더라도 똑같이 놓쳤을 겁니다.
exit code 를 볼 때도 파이프에 걸립니다
고치는 방법은 두 가지입니다. 출력을 기계가 읽는 형식으로 바꾸거나, 아예 출력을 보지 말고 종료 코드로 판정하는 것입니다.
그런데 종료 코드로 바꿀 때도 함정이 하나 더 기다리고 있었습니다.
npx tsc --noEmit -p . | head -5; echo $?
여기서 $? 는 tsc 가 아니라 head 의 종료 코드입니다.
파이프라인은 마지막 명령의 코드를 돌려주기 때문에, 출력을 줄이려고 head 를 붙이는 순간 판정 근거 자체가 바뀌어버립니다.
지금은 출력을 먼저 변수에 담아두고 그 자리에서 코드를 읽습니다.
out=$(npx tsc --noEmit -p . --pretty false 2>&1); code=$?
echo "$out" | head -5
echo "tsc exit=$code"
bash 라면 set -o pipefail 로도 되는데, 어느 쪽이든 요점은 같습니다.
판정하는 값과 보여주는 값을 섞지 않는 것입니다.
검사기를 검사하는 방법
이 일을 겪은 뒤로는 새 검사 명령을 만들면 한 번은 일부러 틀린 코드를 넣어봅니다. 틀린 걸 넣었는데도 검사가 조용하다면, 그 검사는 처음부터 아무 일도 안 하고 있었다는 뜻이니까요.
비용은 30초 남짓인데 이 습관이 잡아주는 건 30초짜리 실수가 아닙니다. 저는 이걸 안 해서 다섯 번의 배포 동안 거짓을 보고했습니다.
이게 타입 검사만의 이야기도 아닙니다. 문자열을 찾아 통과 여부를 판정하는 도구는 전부 같은 위험을 안고 있는데, 찾는 문자열이 안 나오는 이유가 통과일 수도 있지만 애초에 그 문자열이 그런 형태로 안 찍히기 때문일 수도 있어서입니다.
같은 세션에서 비슷한 일을 한 번 더 겪었습니다. 파이썬으로 코드를 치환하는 스크립트를 돌렸는데, 앵커 문자열이 안 맞으면 예외를 던지는 게 아니라 그냥 아무 일도 일어나지 않더군요. 스크립트는 성공한 것처럼 끝나고 파일은 그대로였습니다. 그래서 지금은 치환한 뒤에 결과를 grep 으로 다시 확인합니다.
어디까지 확인한 것인가
2026년 8월 14일과 15일 기준으로 TypeScript 6.0.3 프로젝트 하나에서 확인했습니다.
--pretty false 로 바꾸자 같은 코드에서 에러 6개가 나왔고, 그전까지 grep 은 줄곧 0개를 돌려주고 있었습니다.
어느 버전부터 기본 출력이 이렇게 되는지는 찾아보지 않았고, 다른 도구들도 같은지 하나씩 확인하지도 않았습니다. 제가 확인한 건 제 명령이 거짓을 말하고 있었다는 것과 그걸 고친 방법, 이 둘뿐입니다.