Skip to main content
← 블로그

터미널에서 부르는 Fecit — 단일 실행 파일 CLI

VauDium·

화면으로만 쓰던 Fecit를 터미널에서도 부를 수 있게 했다. 설치도 런타임도 없이 실행 파일 하나로 도는, 앱과 같은 언어를 쓰는 클라이언트를 만든 이야기.

터미널에서 부르는 Fecit — 단일 실행 파일 CLI

Fecit는 화면으로 쓰는 앱입니다. 모바일에서 손가락으로, 데스크톱에서 마우스로. 그런데 화면을 거치지 않고, 키보드에서 손을 떼지 않고 부르고 싶을 때가 있습니다. 그래서 하나를 더 만들었습니다. 터미널에서 부르는 클라이언트, CLI입니다.

할 일 앱에 왜 터미널인가

할 일 관리 앱에 명령줄 도구라니 어울리지 않아 보일 수도 있습니다. 그런데 하루 종일 터미널에 사는 사람에게는, 창을 새로 띄우고 화면을 더듬는 그 몇 초가 흐름을 끊습니다. fecit 한 단어로 끝나는 편이 빠릅니다.

게다가 터미널 도구는 사람만 부르지 않습니다. 스크립트가 부르고, 자동화가 부르고, 다른 도구가 파이프로 이어 부릅니다. 화면용 앱이 닿지 못하는 자리가 거기 있습니다.

지금 할 수 있는 것

CLI는 작게 시작했습니다. 지금은 로그인하고(fecit login), 내 계정을 확인하고(fecit whoami), 로그아웃합니다(fecit logout). 다음은 할 일을 터미널에서 직접 보고 만들고 끝내는 명령들입니다. 모델도 API도 이미 다 있으니, 명령을 얹는 일은 빠릅니다.

앱과 같은 언어를 쓴다

새로 만들면서 가장 든든했던 건, 처음부터 새로 쓸 게 거의 없었다는 점입니다.

Fecit의 데이터 모델은 이미 @fecit/shared라는 공용 패키지에 모여 있습니다. 모바일도, 데스크톱도 같은 모델을 씁니다. CLI도 그걸 그대로 가져다 씁니다. 할 일 하나, 성취 하나를 해석하는 방식이 세 클라이언트에서 완전히 똑같습니다. CLI는 별종이 아니라, 같은 백엔드를 부르는 또 하나의 클라이언트일 뿐입니다.

이게 사실 CLI가 존재할 수 있는 이유이기도 합니다. 화면 뒤에 잘 정리된 API가 있고, 모델이 한곳에 모여 있으면, 새로운 표면을 붙이는 일은 새로 짓는 게 아니라 이어 붙이는 일이 됩니다.

실행 파일 하나

도구는 손에 가벼워야 쓰입니다. 그래서 CLI는 단일 실행 파일로 떨어집니다.

TypeScript로 짠 코드를 Bun이 하나의 바이너리로 묶어 줍니다. bun build --compile 한 줄이면 59MB짜리 실행 파일 하나가 나오고, 그걸 받은 사람은 Node도 Bun도 설치할 필요 없이 그냥 실행합니다. macOS, 리눅스, 윈도우용을 각각 뽑을 수도 있습니다. “이거 쓰려면 뭐부터 깔아야 하죠?“라는 질문이 아예 생기지 않게요.

로그인은 골라서

들어가는 길은 열어 뒀습니다. fecit login을 치면 브라우저가 열리고, 거기서 구글·애플·아이디 셋 중 하나로 로그인합니다. 브라우저 없이 터미널에서 아이디와 비밀번호로 바로 들어갈 수도 있습니다(스크립트에 끼울 때 편합니다). 어느 쪽이든 한 번 들어가면 토큰을 기억해, 다음부터는 바로 부릅니다. 이 로그인을 어떻게 터미널로 데려왔는지는 그 자체로 짧은 이야기가 있어, 다음 글에서 따로 풀겠습니다.

남는 생각

CLI를 하나 붙여 보면 제품의 골격이 드러납니다. 화면을 걷어내도 그 아래에 부를 수 있는 API가 있는가, 데이터를 해석하는 한 벌의 모델이 있는가. Fecit는 마침 그 둘을 갖추고 있었고, 그래서 터미널이라는 새 표면을 다는 데 큰 공사가 필요하지 않았습니다.

화면은 제품의 얼굴이지 전부가 아닙니다. 같은 제품을, 이번엔 한 줄로 부릅니다.