[Fedify] 터미널이 아닌 환경에서는 색상을 빼보자
2025년 8월 6일 오전 12:44
0. 터미널이 아닌 곳에서는 색상을 빼야하는 이유는?
이슈 링크: https://github.com/fedify-dev/fedify/issues/257
PR 링크: https://github.com/fedify-dev/fedify/pull/341

이슈에서는 먼저 ‘Color and TTYs’라는 글을 읽어보기를 권한다.
저 글의 내용을 요약하자면,
터미널에서 색상코드를 출력하는 프로그램을 만들 때, 표준출력이 TTY(=Terminal)에 연결되어있을 때에만 컬러코드를 써야한다고 한다는 내용이다. 터미널이 아닌 곳에서는 색상코드가 제대로 출력되지 않고 깨지기 때문이다.
Fedify CLI에서는 Deno를 활용하는데, Deno에서는 ‘Deno.stdout.isTerminal()’ 명령어를 활용하여 터미널인지 파악할 수 있다고 한다. 이 명령어를 활용하여 현재 사용자가 터미널에 연결되어있는지 확인하고, 아니라면 색상 출력을 비활성화 해보자!
1. 첫 번째 시도
이 이슈를 가져가고 싶다는 댓글을 달 때, 나는 다음과 같은 계획을 말씀드렸다.

위에서 언급한대로 init.ts에서 터미널인지 체크하고, setColorEnabled를 활용하여 색상을 조절했음에도, 터미널이 아닌 환경에서 계속 색상이 출력되었다.
init.ts의 맨 윗부분에서 색상을 수정했었는데, 알고 보니 init.ts는 CLI의 시작점이 아니라 명령어 ‘init’이 실행되는 파일이었다. CLI의 시작점은 init.ts가 아닌 mod.ts였고, 그리고 CLI의 시작점을 찾는 것이 중요한 것이 아니라, 색상을 제어하는 함수를 만들고, 각 명령어가 실행되는 파일들에서 그 함수를 적용하면 되는 일이었다.
@std/fmt/colors의 setColorEnabled()를 통해서 색상 제어를 해줘야했기 때문에 @cliffy/ansi를 import해서 쓰고 있는 colors들을 전부 @std/fmt/colors로 수정해주었다.
import { colors } from "@cliffy/ansi"; → import * as colors from “@std/fmt/colors”;
@cliffy/ansi에서는 색상 적용을 ${colors.bold.green("deno task start")} 와 같이 . 체이닝을 적용해줬었는데, @std/fmt/colors를 적용해줌에 따라 ${colors.bold(colors.green("deno task start"))} 다음과 같이 괄호로 감싸주는 형태로 바꿔주었다.
코드를 적용하니 inbox 명령어에서는 터미널이 아닐 때 색상이 빠져서 출력이 되었다. 하지만, lookup 명령어에서는 여전히 색상이 포함되어서 출력이 되었다.
‘deno task cli inbox’ 명령어 실행 시

‘deno task cli inbox > inbox.txt’ 실행 시

‘deno task cli lookup (handle) > lookup.txt’ 실행

@std/fmt/colors의 setColorEnabled()를 활용하여 색상을 제어했지만, Deno의 console.log 자체에서 색상을 조절하는 것은 제어에 실패한 것 같았다. 해결점을 찾지 못한채, 지금까지의 변경점을 토대로 PR을 작성하고, push했다.
2. 피드백을 바탕으로 한 두번째 시도

PR을 올리자 멘토님께서는 Deno.inspect로 console.log의 색상을 조절할 수 있다고 말씀해주셨다. 말씀해주신 내용을 바탕으로 헬퍼 함수를 만들었다.
export function formatObjectForOutput(obj: unknown): string {
if (colorEnabled) {
return Deno.inspect(obj, { colors: true });
} else {
return Deno.inspect(obj, { colors: false });
}
}
colorEnabled 여부에 따라 Deno.inspect를 활용하여 색상 출력 여부를 결정했다.
그리고 감사하게도 다른 멘티분께서 리뷰를 해주셨는데, 터미널에서 뿐만 아니라 lookup.ts에서 ‘-o’ 명령어를 통해서 options.output이 실행되었을 때에도 색상을 빼야한다는 사실을 알려주셨다.

기존 lookup.ts에서는 output option이 선택되었는지에 따라 색상을 제어했었는데, 거기에 colorEnabled 여부도 같이 고려해서 헬퍼 함수를 사용했다.

lookup에서 output 옵션 선택 여부를 헬퍼 함수에 전달하기 위해 파라미터에 colors를 받을 수 있도록 구조를 다음과 같이 변경했다.
export const colorEnabled: boolean = Deno.stdout.isTerminal() &&
!Deno.env.has("NO_COLOR");
export function formatCliObjectOutputWithColor(
obj: unknown,
colors?: boolean,
): string {
const enableColors = colors ?? colorEnabled;
return Deno.inspect(obj, { colors: enableColors });
}
참고로, colorEnabled와 헬퍼 함수는 CLI의 시작점인 mod.ts에 위치하고 있었는데 테스트를 돌려보니 circular dependency 문제가 발생해서 utils.ts로 코드들을 옮겼다.

해당 코드를 적용하고 다시 'deno task cli lookup (handle) > lookup.txt’ 명령어를 적용했더니 색상이 빠진 채 출력이 되었다!
CLI의 시작점에서 사용자가 터미널인지 확인한 후 색상 옵션만 끄면 해결되리라 기대했지만, 생각보다 건드릴 부분이 많았던 이슈였다. CLI에서 어떤 라이브러리를 활용하여 색상을 제어하는지, 그리고 Fedify의 각 명령어가 어떻게 작동하는지 구조를 살펴볼 수 있었던 좋은 기회였다.