본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
AI Generated
2025. 12. 5. · 56 Views
TUI 터미널 사용자 인터페이스 구현 완벽 가이드
SolidJS 기반의 OpenTUI 프레임워크를 활용하여 터미널에서 동작하는 현대적인 사용자 인터페이스를 구축하는 방법을 알아봅니다. CLI 도구를 넘어 인터랙티브한 TUI 애플리케이션 개발의 핵심을 다룹니다.
목차
- cli/cmd/tui 디렉토리 구조
- @opentui/solid 프레임워크
- SolidJS 기반 터미널 컴포넌트
- app.tsx 메인 애플리케이션
- 키바인드와 단축키 시스템
- 테마와 스타일링
1. cli/cmd/tui 디렉토리 구조
어느 날 김개발 씨는 회사에서 새로운 CLI 도구를 만들어 달라는 요청을 받았습니다. 그런데 단순한 명령어 도구가 아니라, 터미널에서 마우스와 키보드로 조작할 수 있는 인터랙티브한 인터페이스가 필요했습니다.
"터미널에서 GUI 같은 걸 어떻게 만들지?" 김개발 씨는 고민에 빠졌습니다.
**TUI(Terminal User Interface)**는 터미널 환경에서 동작하는 그래픽 사용자 인터페이스입니다. 마치 웹 프로젝트가 src, components, pages 폴더로 구성되듯이, TUI 프로젝트도 체계적인 디렉토리 구조가 필요합니다.
잘 설계된 폴더 구조는 코드의 가독성과 유지보수성을 크게 높여줍니다.
다음 코드를 살펴봅시다.
cli/
├── cmd/
│ └── tui/
│ ├── app.tsx // 메인 애플리케이션 진입점
│ ├── components/ // 재사용 가능한 UI 컴포넌트
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ └── StatusBar.tsx
│ ├── screens/ // 화면 단위 컴포넌트
│ │ ├── HomeScreen.tsx
│ │ └── SettingsScreen.tsx
│ ├── hooks/ // 커스텀 훅
│ │ └── useKeyboard.ts
│ ├── themes/ // 테마 설정
│ │ └── default.ts
│ └── utils/ // 유틸리티 함수
│ └── terminal.ts
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 팀장님이 "우리 회사 내부 도구를 터미널에서 사용할 수 있게 만들어줘"라고 요청했을 때, 처음에는 단순한 CLI를 생각했습니다.
하지만 요구사항을 자세히 들어보니, 메뉴를 선택하고 데이터를 입력받는 복잡한 인터페이스가 필요했습니다. 선배 개발자 박시니어 씨가 다가와 조언합니다.
"TUI를 만들어야겠네. 요즘은 좋은 프레임워크가 많아서 웹 개발하듯이 만들 수 있어." 그렇다면 TUI 프로젝트의 디렉토리 구조는 어떻게 잡아야 할까요?
쉽게 비유하자면, 디렉토리 구조는 마치 잘 정리된 서랍장과 같습니다. 양말은 양말 서랍에, 셔츠는 셔츠 서랍에 넣어두면 필요할 때 바로 찾을 수 있습니다.
코드도 마찬가지입니다. 컴포넌트는 components 폴더에, 화면은 screens 폴더에 정리해두면 어떤 개발자가 와도 쉽게 코드를 찾을 수 있습니다.
cli/cmd/tui 경로를 살펴보겠습니다. cli는 명령줄 인터페이스 관련 코드를 담는 최상위 폴더입니다.
cmd는 실행 가능한 명령어들을 모아두는 곳이고, tui는 터미널 UI 관련 코드가 위치합니다. 가장 중요한 파일은 app.tsx입니다.
웹에서 index.js가 진입점인 것처럼, TUI에서는 app.tsx가 애플리케이션의 시작점 역할을 합니다. 모든 컴포넌트와 화면이 이 파일을 통해 렌더링됩니다.
components 폴더에는 재사용 가능한 작은 UI 조각들이 들어갑니다. Header, Sidebar, StatusBar처럼 여러 화면에서 공통으로 사용하는 컴포넌트를 이곳에 배치합니다.
screens 폴더는 전체 화면 단위의 컴포넌트를 담습니다. 웹에서 페이지 컴포넌트와 비슷한 개념입니다.
HomeScreen, SettingsScreen처럼 사용자가 보는 각 화면을 독립적인 파일로 관리합니다. hooks 폴더는 커스텀 훅을 모아두는 곳입니다.
키보드 입력 처리, 터미널 크기 감지 같은 로직을 재사용 가능한 훅으로 만들어 관리합니다. themes 폴더에는 색상, 스타일 관련 설정이 들어갑니다.
터미널도 다양한 색상과 스타일을 지원하므로, 테마를 별도로 관리하면 일관된 디자인을 유지할 수 있습니다. 박시니어 씨의 설명을 들은 김개발 씨가 물었습니다.
"웹 프로젝트 구조랑 정말 비슷하네요?" "맞아. 그래서 웹 개발 경험이 있으면 TUI 개발도 금방 적응할 수 있어." 이렇게 체계적인 구조를 잡아두면 프로젝트가 커져도 코드를 쉽게 관리할 수 있습니다.
새로운 팀원이 합류해도 어디에 무엇이 있는지 금방 파악할 수 있습니다.
실전 팁
💡 - 폴더 구조는 프로젝트 초기에 잘 잡아두면 나중에 리팩토링 비용을 크게 줄일 수 있습니다
- 파일명은 PascalCase로 컴포넌트임을 명확히 표시하세요
2. @opentui/solid 프레임워크
김개발 씨가 TUI 개발을 시작하려고 구글링을 해보니 여러 프레임워크가 나왔습니다. Ink, Blessed, Terminal-kit 등 선택지가 많아 혼란스러웠습니다.
그때 박시니어 씨가 추천한 것이 바로 @opentui/solid였습니다. "SolidJS 문법 그대로 터미널 UI를 만들 수 있어.
React 경험이 있으면 더 쉽고."
@opentui/solid는 SolidJS의 반응형 시스템을 터미널 환경에 가져온 프레임워크입니다. 웹에서 사용하던 JSX 문법, 시그널, 이펙트를 그대로 활용할 수 있어 학습 곡선이 완만합니다.
가상 DOM 없이 세밀한 반응형 업데이트가 가능해 터미널에서도 빠른 렌더링 성능을 보여줍니다.
다음 코드를 살펴봅시다.
// package.json 의존성 설치
// npm install @opentui/solid solid-js
import { render } from '@opentui/solid';
import { createSignal } from 'solid-js';
// 간단한 카운터 TUI 애플리케이션
function Counter() {
const [count, setCount] = createSignal(0);
// 1초마다 카운터 증가
setInterval(() => setCount(c => c + 1), 1000);
return (
<box border={{ type: 'single' }}>
<text>현재 카운트: {count()}</text>
</box>
);
}
// 터미널에 렌더링
render(() => <Counter />);
김개발 씨가 @opentui/solid 문서를 펼쳐보니 익숙한 코드들이 눈에 들어왔습니다. createSignal, createEffect, JSX 문법...
모두 React나 SolidJS에서 본 것들이었습니다. "이게 정말 터미널에서 동작해요?" 김개발 씨가 의아해하며 물었습니다.
박시니어 씨가 웃으며 대답합니다. "응, 웹 브라우저 대신 터미널이 렌더링 대상이 되는 거야.
원리는 같아." 그렇다면 @opentui/solid의 핵심 개념을 살펴보겠습니다. 이 프레임워크는 마치 통역사와 같습니다.
우리가 익숙한 JSX 언어로 말하면, 프레임워크가 터미널이 이해할 수 있는 ANSI 이스케이프 코드로 번역해줍니다. 덕분에 개발자는 복잡한 터미널 제어 코드를 몰라도 UI를 만들 수 있습니다.
render 함수는 프레임워크의 시작점입니다. 웹에서 ReactDOM.render나 createRoot가 하는 역할과 동일합니다.
컴포넌트를 받아서 터미널 화면에 그려줍니다. createSignal은 SolidJS의 핵심인 반응형 상태 관리 도구입니다.
값이 변경되면 해당 값을 사용하는 부분만 정확히 다시 렌더링됩니다. React의 useState와 비슷하지만, 가상 DOM 비교 없이 직접 업데이트되어 더 효율적입니다.
코드를 자세히 살펴보면, box와 text라는 특별한 요소가 보입니다. 웹에서 div와 span을 사용하듯이, TUI에서는 box와 text를 사용합니다.
box는 테두리를 가질 수 있는 컨테이너이고, text는 문자열을 표시합니다. border={{ type: 'single' }} 속성은 박스에 단일 선 테두리를 그립니다.
터미널에서 사용할 수 있는 유니코드 박스 드로잉 문자를 활용해 깔끔한 테두리를 만들어줍니다. 김개발 씨가 코드를 실행해보니 정말로 터미널에 테두리가 있는 박스가 나타났습니다.
그리고 그 안에서 숫자가 1초마다 증가하고 있었습니다. "와, 진짜 되네요!" 이 프레임워크의 장점은 웹 개발 지식을 그대로 활용할 수 있다는 점입니다.
컴포넌트 분리, 상태 관리, 조건부 렌더링 등 익숙한 패턴을 모두 사용할 수 있습니다. 다만 주의할 점이 있습니다.
터미널 환경은 웹과 다르게 고정된 문자 그리드에서 동작합니다. 픽셀 단위가 아닌 행과 열 단위로 레이아웃을 생각해야 합니다.
실전 팁
💡 - 터미널 크기는 process.stdout.columns와 process.stdout.rows로 확인할 수 있습니다
- 개발 중에는 터미널 에뮬레이터의 크기를 고정해두면 디버깅이 편합니다
3. SolidJS 기반 터미널 컴포넌트
기본적인 렌더링에 성공한 김개발 씨는 이제 본격적으로 컴포넌트를 만들기 시작했습니다. "웹에서 Button, Input 컴포넌트를 만들던 것처럼 하면 되겠지?" 맞습니다.
다만 터미널 환경에 맞는 몇 가지 특별한 요소들을 알아야 합니다.
터미널 컴포넌트는 웹 컴포넌트와 유사하지만 box, text, list 같은 터미널 전용 요소를 사용합니다. 각 요소는 위치, 크기, 스타일 속성을 받아 터미널 화면의 특정 영역에 렌더링됩니다.
컴포넌트를 조합하여 복잡한 UI를 구성할 수 있습니다.
다음 코드를 살펴봅시다.
import { createSignal, For } from 'solid-js';
// 메뉴 항목 타입 정의
interface MenuItem {
id: string;
label: string;
}
// 선택 가능한 리스트 컴포넌트
function SelectableList(props: { items: MenuItem[] }) {
const [selected, setSelected] = createSignal(0);
return (
<box width="100%" height="100%" border={{ type: 'double' }}>
<text bold color="cyan">메뉴를 선택하세요</text>
<For each={props.items}>
{(item, index) => (
<text
top={index() + 1}
color={selected() === index() ? 'green' : 'white'}
bold={selected() === index()}
>
{selected() === index() ? '> ' : ' '}{item.label}
</text>
)}
</For>
</box>
);
}
김개발 씨는 메뉴 화면을 만들어야 했습니다. 여러 항목 중 하나를 선택하는 단순한 기능이지만, 터미널에서는 어떻게 구현해야 할지 막막했습니다.
박시니어 씨가 힌트를 줍니다. "웹에서 리스트 만드는 것과 똑같아.
다만 div 대신 box, span 대신 text를 쓰는 것만 기억해." 터미널 컴포넌트의 기본 요소들을 알아보겠습니다. box는 가장 기본적인 컨테이너 요소입니다.
마치 웹의 div와 같은 역할을 합니다. width, height로 크기를 지정하고, border로 테두리를 그릴 수 있습니다.
터미널에서 영역을 나누고 레이아웃을 잡을 때 핵심적으로 사용됩니다. text는 문자열을 표시하는 요소입니다.
웹의 span과 비슷합니다. color 속성으로 글자 색상을, bold 속성으로 굵게 표시할 수 있습니다.
top, left 속성으로 부모 요소 내에서의 위치를 지정합니다. 코드에서 For 컴포넌트가 보입니다.
이것은 SolidJS의 리스트 렌더링 유틸리티입니다. React의 map과 비슷하지만, 각 항목의 변경을 개별적으로 추적해 더 효율적으로 업데이트합니다.
selected() === index() 부분을 주목해보세요. SolidJS에서 시그널 값을 읽을 때는 함수 호출 형태로 사용합니다.
이것이 React의 useState와 다른 점입니다. 괄호를 빼먹으면 값이 아닌 함수 자체가 반환되므로 주의해야 합니다.
조건부 스타일링도 웹과 동일한 패턴입니다. 선택된 항목은 초록색으로, 그렇지 않은 항목은 흰색으로 표시됩니다.
선택된 항목 앞에는 > 기호를 붙여 시각적으로 구분합니다. 터미널 환경의 특수성도 고려해야 합니다.
웹과 달리 터미널은 지원하는 색상이 제한적입니다. 기본적으로 16색, 확장된 터미널은 256색이나 트루컬러를 지원합니다.
따라서 color 속성에는 보통 'red', 'green', 'blue' 같은 기본 색상명을 사용합니다. 김개발 씨가 컴포넌트를 완성하고 실행해보니, 터미널에 메뉴가 깔끔하게 표시되었습니다.
선택된 항목이 초록색으로 강조되어 한눈에 알아볼 수 있었습니다.
실전 팁
💡 - 터미널 색상은 사용자의 테마에 따라 다르게 보일 수 있으니 대비를 충분히 주세요
- props 타입을 명확히 정의하면 컴포넌트 재사용성이 높아집니다
4. app.tsx 메인 애플리케이션
컴포넌트들을 만들었으니 이제 이것들을 조합해서 완성된 애플리케이션을 구성할 차례입니다. 김개발 씨는 app.tsx 파일을 열었습니다.
"여기서 모든 것을 연결하면 되는 거죠?" 박시니어 씨가 고개를 끄덕였습니다. "맞아.
앱의 전체 구조와 상태를 관리하는 핵심 파일이야."
app.tsx는 TUI 애플리케이션의 진입점이자 전체 구조를 정의하는 파일입니다. 여기서 화면 전환 로직, 전역 상태, 키보드 이벤트 리스너를 설정합니다.
웹에서 App.js가 라우터와 레이아웃을 구성하듯이, TUI에서도 app.tsx가 동일한 역할을 수행합니다.
다음 코드를 살펴봅시다.
import { render } from '@opentui/solid';
import { createSignal, Switch, Match } from 'solid-js';
import { Header } from './components/Header';
import { HomeScreen } from './screens/HomeScreen';
import { SettingsScreen } from './screens/SettingsScreen';
type Screen = 'home' | 'settings';
function App() {
const [currentScreen, setCurrentScreen] = createSignal<Screen>('home');
// 화면 전환 함수
const navigate = (screen: Screen) => setCurrentScreen(screen);
return (
<box width="100%" height="100%">
<Header onNavigate={navigate} />
<box top={3} width="100%" height="calc(100% - 3)">
<Switch>
<Match when={currentScreen() === 'home'}>
<HomeScreen />
</Match>
<Match when={currentScreen() === 'settings'}>
<SettingsScreen />
</Match>
</Switch>
</box>
</box>
);
}
render(() => <App />);
김개발 씨는 app.tsx 파일이 왜 중요한지 궁금했습니다. 컴포넌트만 잘 만들면 되는 것 아닌가요?
박시니어 씨가 설명합니다. "app.tsx는 마치 건물의 설계도와 같아.
각 방(컴포넌트)을 어디에 배치하고, 어떻게 이동할 수 있는지 정의하는 곳이지." 코드의 구조를 살펴보겠습니다. 먼저 Screen 타입을 정의했습니다.
'home'과 'settings' 두 가지 화면이 있음을 명시합니다. TypeScript를 사용하면 존재하지 않는 화면으로 이동하려는 실수를 컴파일 타임에 잡아낼 수 있습니다.
currentScreen 시그널은 현재 어떤 화면을 보여줄지 결정합니다. 초기값은 'home'입니다.
사용자가 다른 화면으로 이동하면 이 값이 바뀌고, 자동으로 화면이 전환됩니다. navigate 함수는 화면 전환을 담당합니다.
단순히 setCurrentScreen을 호출하는 래퍼 함수입니다. 이렇게 별도 함수로 분리하면 나중에 전환 애니메이션이나 로깅 같은 기능을 추가하기 쉽습니다.
레이아웃 구조를 보면, 전체를 감싸는 box 안에 Header와 콘텐츠 영역이 있습니다. Header에 onNavigate 콜백을 전달하여 헤더에서 화면 전환을 트리거할 수 있게 했습니다.
Switch와 Match는 SolidJS의 조건부 렌더링 컴포넌트입니다. React에서 삼항 연산자나 && 연산자를 사용하던 것보다 더 선언적으로 조건을 표현할 수 있습니다.
currentScreen 값에 따라 HomeScreen이나 SettingsScreen 중 하나만 렌더링됩니다. **top={3}**과 height="calc(100% - 3)" 부분에 주목해보세요.
Header가 3줄을 차지한다고 가정하고, 콘텐츠 영역을 그 아래에 배치합니다. 터미널에서는 픽셀이 아닌 행 단위로 위치를 지정합니다.
김개발 씨가 코드를 실행하니 상단에 헤더가, 그 아래에 메인 콘텐츠가 표시되었습니다. 헤더의 메뉴를 선택하니 화면이 전환되었습니다.
웹 SPA와 똑같은 경험이었습니다. 하지만 한 가지 문제가 있었습니다.
마우스로 클릭할 수 없었습니다. 터미널에서 화면 전환을 하려면 키보드를 사용해야 합니다.
다음 섹션에서 키바인드를 다루겠습니다.
실전 팁
💡 - 화면이 많아지면 라우터 라이브러리를 별도로 만들어 관리하는 것이 좋습니다
- 전역 상태는 Context API나 별도 스토어로 분리하면 컴포넌트 간 데이터 공유가 편합니다
5. 키바인드와 단축키 시스템
김개발 씨가 만든 TUI는 화면이 예쁘게 나왔지만 조작할 방법이 없었습니다. 마우스는 터미널에서 제한적으로만 동작합니다.
"vim처럼 키보드로 모든 걸 조작할 수 있게 만들어야 해." 박시니어 씨의 조언에 김개발 씨는 키바인드 시스템을 구현하기 시작했습니다.
키바인드 시스템은 TUI의 핵심입니다. 사용자가 키보드로 메뉴를 탐색하고, 항목을 선택하고, 화면을 전환할 수 있게 합니다.
Node.js의 readline 모듈이나 프레임워크가 제공하는 키보드 이벤트를 활용하여 구현합니다. 직관적인 단축키 설계가 사용자 경험을 좌우합니다.
다음 코드를 살펴봅시다.
import { createSignal, onMount, onCleanup } from 'solid-js';
import { useInput } from '@opentui/solid';
function NavigableMenu() {
const [selectedIndex, setSelectedIndex] = createSignal(0);
const items = ['파일 열기', '저장하기', '설정', '종료'];
// 키보드 입력 처리
useInput((input, key) => {
// 위/아래 화살표 또는 j/k로 이동
if (key.upArrow || input === 'k') {
setSelectedIndex(i => Math.max(0, i - 1));
}
if (key.downArrow || input === 'j') {
setSelectedIndex(i => Math.min(items.length - 1, i + 1));
}
// Enter로 선택
if (key.return) {
handleSelect(items[selectedIndex()]);
}
// q로 종료
if (input === 'q') {
process.exit(0);
}
});
const handleSelect = (item: string) => {
console.log(`선택됨: ${item}`);
};
return (/* 렌더링 로직 */);
}
터미널 환경에서 키보드는 마우스를 대신하는 유일한 입력 도구입니다. 따라서 키바인드 설계가 TUI 사용성의 80%를 결정한다고 해도 과언이 아닙니다.
김개발 씨가 vim을 처음 접했을 때를 떠올려봅시다. h, j, k, l로 커서를 움직이는 것이 처음에는 낯설었지만, 익숙해지니 마우스보다 빨랐습니다.
좋은 TUI도 마찬가지입니다. useInput 훅은 @opentui/solid가 제공하는 키보드 이벤트 리스너입니다.
두 개의 인자를 받습니다. input은 입력된 문자 그 자체이고, key는 특수 키 정보를 담은 객체입니다.
key 객체에는 upArrow, downArrow, leftArrow, rightArrow 같은 방향키 정보가 있습니다. return은 Enter 키, escape는 ESC 키를 의미합니다.
이런 특수 키들은 일반 문자와 다르게 처리해야 합니다. 코드에서 이중 바인딩을 주목해보세요.
위로 이동할 때 upArrow와 'k' 둘 다 사용할 수 있습니다. vim 사용자를 위한 배려입니다.
이렇게 여러 키에 같은 기능을 바인딩하면 다양한 사용자 취향을 수용할 수 있습니다. Math.max와 Math.min은 인덱스가 범위를 벗어나지 않도록 보호합니다.
첫 번째 항목에서 위로 가려 해도 0 미만이 되지 않고, 마지막 항목에서 아래로 가려 해도 배열 길이를 넘지 않습니다. 'q' 키로 종료하는 것은 TUI의 관례입니다.
대부분의 터미널 애플리케이션이 q나 Ctrl+C로 종료됩니다. 사용자가 기대하는 동작을 따르면 학습 비용이 줄어듭니다.
실무에서는 더 복잡한 단축키가 필요합니다. Ctrl+S로 저장, Ctrl+Q로 종료, Tab으로 다음 필드 이동 등.
이런 조합 키는 key 객체의 ctrl, shift, meta 속성으로 감지합니다. 김개발 씨가 키바인드를 구현하고 나니 TUI가 비로소 살아 움직이기 시작했습니다.
화살표 키로 메뉴를 오르내리고, Enter로 선택하는 것이 자연스러웠습니다. 주의할 점이 있습니다.
너무 많은 단축키는 오히려 사용성을 해칩니다. 핵심 기능에만 단축키를 할당하고, 나머지는 메뉴를 통해 접근하게 하는 것이 좋습니다.
실전 팁
💡 - 단축키 도움말을 화면 하단에 표시하면 사용자가 기능을 쉽게 발견할 수 있습니다
- Ctrl+C는 항상 종료에 예약하세요. 이것을 다른 용도로 쓰면 사용자가 혼란스러워합니다
6. 테마와 스타일링
기능은 완성되었지만 김개발 씨의 TUI는 밋밋해 보였습니다. 흰색 글자에 검은 배경, 모든 요소가 똑같은 스타일이었습니다.
"사용자가 테마를 바꿀 수 있게 하면 어떨까?" 박시니어 씨의 제안에 김개발 씨는 테마 시스템을 설계하기 시작했습니다.
테마 시스템은 TUI의 시각적 일관성을 담당합니다. 색상, 테두리 스타일, 강조 방식 등을 중앙에서 관리하면 전체 애플리케이션의 룩앤필을 쉽게 변경할 수 있습니다.
다크 모드, 라이트 모드, 고대비 모드 등 다양한 테마를 제공하면 접근성도 높아집니다.
다음 코드를 살펴봅시다.
// themes/default.ts
export const defaultTheme = {
colors: {
primary: 'cyan',
secondary: 'gray',
success: 'green',
error: 'red',
warning: 'yellow',
text: 'white',
background: 'black',
},
border: {
type: 'single' as const, // 'single', 'double', 'round'
color: 'cyan',
},
highlight: {
background: 'blue',
text: 'white',
bold: true,
},
};
// ThemeContext.tsx
import { createContext, useContext } from 'solid-js';
const ThemeContext = createContext(defaultTheme);
export function ThemeProvider(props: { theme: typeof defaultTheme; children: any }) {
return (
<ThemeContext.Provider value={props.theme}>
{props.children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);
테마 시스템이 왜 필요할까요? 김개발 씨가 만든 TUI를 다른 팀에서도 사용하고 싶어했습니다.
하지만 그 팀은 회사 브랜드 색상을 적용하고 싶어했습니다. 테마 시스템이 없었다면 모든 컴포넌트를 일일이 수정해야 했을 것입니다.
테마는 마치 옷과 같습니다. 같은 사람이라도 어떤 옷을 입느냐에 따라 분위기가 달라집니다.
TUI도 마찬가지입니다. 같은 기능이라도 테마에 따라 전혀 다른 느낌을 줄 수 있습니다.
defaultTheme 객체를 살펴보겠습니다. colors 섹션에는 용도별 색상이 정의되어 있습니다.
primary는 주요 강조색, error는 오류 메시지에 사용합니다. 이렇게 의미 기반으로 색상을 정의하면, 나중에 테마를 바꿀 때 일관성을 유지하기 쉽습니다.
border 섹션은 테두리 관련 설정입니다. 터미널에서는 유니코드 박스 드로잉 문자로 테두리를 그립니다.
'single'은 단일 선, 'double'은 이중 선, 'round'는 둥근 모서리를 의미합니다. highlight 섹션은 선택된 항목이나 포커스된 요소의 스타일을 정의합니다.
배경색, 글자색, 굵기를 함께 지정하여 눈에 띄게 만듭니다. Context API를 사용하면 테마를 전역으로 공유할 수 있습니다.
ThemeProvider로 앱 전체를 감싸면, 어떤 컴포넌트에서든 useTheme으로 테마 값에 접근할 수 있습니다. props로 일일이 전달할 필요가 없습니다.
실제 컴포넌트에서는 이렇게 사용합니다. const theme = useTheme()로 테마를 가져온 뒤, color={theme.colors.primary}처럼 적용합니다.
테마가 바뀌면 모든 컴포넌트가 자동으로 새 스타일을 적용받습니다. 고대비 테마도 고려해보세요.
시력이 좋지 않은 사용자를 위해 색상 대비를 극대화한 테마를 제공하면 접근성이 향상됩니다. 터미널 환경에서는 특히 중요합니다.
김개발 씨는 다크 테마와 라이트 테마를 만들어 사용자가 선택할 수 있게 했습니다. 설정 화면에서 테마를 바꾸면 전체 앱이 즉시 새로운 모습으로 변했습니다.
사용자들의 반응은 뜨거웠습니다.
실전 팁
💡 - 터미널마다 색상 렌더링이 다르니 여러 환경에서 테스트해보세요
- 사용자 설정을 파일로 저장하면 다음 실행 시에도 테마가 유지됩니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
UX와 협업 패턴 완벽 가이드
AI 에이전트와 사용자 간의 효과적인 협업을 위한 UX 패턴을 다룹니다. 프롬프트 핸드오프부터 인터럽트 처리까지, 현대적인 에이전트 시스템 설계의 핵심을 배웁니다.
자가 치유 및 재시도 패턴 완벽 가이드
AI 에이전트와 분산 시스템에서 필수적인 자가 치유 패턴을 다룹니다. 에러 감지부터 서킷 브레이커까지, 시스템을 스스로 복구하는 탄력적인 코드 작성법을 배워봅니다.
Feedback Loops 컴파일러와 CI/CD 완벽 가이드
컴파일러 피드백 루프부터 CI/CD 파이프라인, 테스트 자동화, 자가 치유 빌드까지 현대 개발 워크플로우의 핵심을 다룹니다. 초급 개발자도 쉽게 이해할 수 있도록 실무 예제와 함께 설명합니다.
실전 MCP 통합 프로젝트 완벽 가이드
Model Context Protocol을 활용한 실전 통합 프로젝트를 처음부터 끝까지 구축하는 방법을 다룹니다. 아키텍처 설계부터 멀티 서버 통합, 모니터링, 배포까지 운영 레벨의 MCP 시스템을 구축하는 노하우를 담았습니다.
MCP 동적 도구 업데이트 완벽 가이드
AI 에이전트의 도구를 런타임에 동적으로 로딩하고 관리하는 방법을 알아봅니다. 플러그인 시스템 설계부터 핫 리로딩, 보안까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.