Crohasang Logo
#10

[Fedify] 터미널이 아닌 환경에서는 색상을 빼보자

2025년 8월 6일 오전 12:44

0. 터미널이 아닌 곳에서는 색상을 빼야하는 이유는?

이슈 링크: https://github.com/fedify-dev/fedify/issues/257

PR 링크: https://github.com/fedify-dev/fedify/pull/341

image 1

이슈에서는 먼저 ‘Color and TTYs’라는 글을 읽어보기를 권한다.

저 글의 내용을 요약하자면,

터미널에서 색상코드를 출력하는 프로그램을 만들 때, 표준출력이 TTY(=Terminal)에 연결되어있을 때에만 컬러코드를 써야한다고 한다는 내용이다. 터미널이 아닌 곳에서는 색상코드가 제대로 출력되지 않고 깨지기 때문이다.

Fedify CLI에서는 Deno를 활용하는데, Deno에서는 ‘Deno.stdout.isTerminal()’ 명령어를 활용하여 터미널인지 파악할 수 있다고 한다. 이 명령어를 활용하여 현재 사용자가 터미널에 연결되어있는지 확인하고, 아니라면 색상 출력을 비활성화 해보자!


1. 첫 번째 시도

이 이슈를 가져가고 싶다는 댓글을 달 때, 나는 다음과 같은 계획을 말씀드렸다.

image 2

위에서 언급한대로 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’ 명령어 실행 시

image 3

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

image 4

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

image 5

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


2. 피드백을 바탕으로 한 두번째 시도

image 6

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이 실행되었을 때에도 색상을 빼야한다는 사실을 알려주셨다.

image 7

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

image 8

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로 코드들을 옮겼다.

image 9

해당 코드를 적용하고 다시 'deno task cli lookup (handle) > lookup.txt’ 명령어를 적용했더니 색상이 빠진 채 출력이 되었다!

CLI의 시작점에서 사용자가 터미널인지 확인한 후 색상 옵션만 끄면 해결되리라 기대했지만, 생각보다 건드릴 부분이 많았던 이슈였다. CLI에서 어떤 라이브러리를 활용하여 색상을 제어하는지, 그리고 Fedify의 각 명령어가 어떻게 작동하는지 구조를 살펴볼 수 있었던 좋은 기회였다.