기능 개요: 자동 포맷팅의 마법
학습 후 할 수 있는 것
- 플러그인의 8가지 핵심 기능 이해
- 이 플러그인이 적합한 시나리오 파악
- 플러그인의 한계(할 수 없는 것) 이해
현재 겪고 있는 문제
플러그인 정보
이 플러그인의 전체 이름은 @franlol/opencode-md-table-formatter이며, 이하 "테이블 포맷팅 플러그인"이라고 합니다.
AI가 생성한 Markdown 테이블은 종종 다음과 같습니다:
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |열 너비가 고르지 않아 보기 불편합니다. 수동으로 조정하시겠습니까? AI가 새 테이블을 생성할 때마다 매번 조정해야 하니 너무 피곤합니다.
언제 사용해야 하나요
- AI가 Markdown 테이블을 생성했고 더 깔끔하게 만들고 싶을 때
- OpenCode의 숨김 모드(Concealment Mode)를 활성화했고 테이블 정렬에 문제가 있을 때
- 테이블 열 너비를 수동으로 조정하기 귀찮을 때
핵심 아이디어
이 플러그인의 작동 원리는 매우 간단합니다:
AI 텍스트 생성 → 플러그인 테이블 감지 → 구조 검증 → 포맷팅 → 미화된 텍스트 반환이 플러그인은 OpenCode의 experimental.text.complete 훅에 마운트되어 있어 AI가 텍스트 생성을 완료할 때마다 자동으로 처리됩니다. 수동으로 트리거할 필요가 없으며, 전 과정이 자동으로 진행됩니다.
8가지 핵심 기능
1. 자동 테이블 포맷팅
플러그인은 AI가 생성한 텍스트에서 Markdown 테이블을 자동으로 감지하고 열 너비를 통일하여 테이블을 깔끔하고 아름답게 만듭니다.
포맷팅 전:
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |포맷팅 후:
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |트리거 조건
플러그인은 experimental.text.complete 훅에 마운트되어 있어 AI가 텍스트 생성을 완료하면 자동으로 트리거되며, 수동 조작이 필요하지 않습니다.
2. 숨김 모드 호환
OpenCode는 기본적으로 숨김 모드(Concealment Mode)를 활성화하며, Markdown 기호(예: **, *, ~~)를 숨깁니다.
일반적인 테이블 포맷팅 도구는 이 점을 고려하지 않아 너비를 계산할 때 **도 함께 계산하여 정렬이 어긋나는 문제가 발생합니다.
이 플러그인은 숨김 모드에 최적화되어 있습니다:
- 너비를 계산할 때
**굵게**,*기울임*,~~취소선~~등의 기호를 제거 - 출력할 때 원본 Markdown 구문 유지
- 최종 효과: 숨김 모드에서 테이블이 완벽하게 정렬됨
기술 세부사항: 너비 계산 로직
// Markdown 기호 제거(너비 계산용)
visualText = visualText
.replace(/\*\*\*(.+?)\*\*\*/g, "$1") // ***굵은기울임*** → 텍스트
.replace(/\*\*(.+?)\*\*/g, "$1") // **굵게** → 굵게
.replace(/\*(.+?)\*/g, "$1") // *기울임* → 기울임
.replace(/~~(.+?)~~/g, "$1") // ~~취소선~~ → 취소선소스 코드 위치: index.ts:181-185
3. 정렬 지원
Markdown 테이블의 세 가지 정렬 방식을 지원합니다:
| 구문 | 정렬 방식 | 효과 |
|---|---|---|
--- 또는 :--- | 왼쪽 정렬 | 텍스트 왼쪽(두 구문의 효과는 동일) |
:---: | 가운데 정렬 | 텍스트 가운데 |
---: | 오른쪽 정렬 | 텍스트 오른쪽 |
예시:
| 왼쪽 정렬 | 가운데 정렬 | 오른쪽 정렬 |
|--- | --- | ---|
| 텍스트 | 텍스트 | 텍스트 |포맷팅 후 각 열은 지정된 방식으로 정렬되며, 구분 행은 정렬 방식에 따라 다시 생성됩니다.
4. 중첩 Markdown 처리
테이블 셀 안에는 ***굵은기울임***과 같은 중첩된 Markdown 구문이 있을 수 있습니다.
플러그인은 다중 라운드 정규식 알고리즘을 사용하여 바깥에서 안으로 순차적으로 제거합니다:
***굵은기울임*** → **굵은기울임** → *굵은기울임* → 굵은기울임이렇게 하면 여러 레벨이 중첩되어도 너비 계산이 정확합니다.
5. 코드 블록 보호
인라인 코드(백틱으로 감싸진) 안의 Markdown 기호는 원본 그대로 유지되어야 하며, 제거되지 않아야 합니다.
예를 들어 `**bold**`의 경우 사용자가 보는 것은 **bold**라는 8개의 문자이지 bold라는 4개의 문자가 아닙니다.
플러그인은 먼저 코드 블록 내용을 추출하고, 다른 부분의 Markdown 기호를 제거한 후 코드 블록 내용을 다시 넣습니다.
기술 세부사항: 코드 블록 보호 로직
// 1단계: 인라인 코드 추출 및 보호
const codeBlocks: string[] = []
let textWithPlaceholders = text.replace(/`(.+?)`/g, (match, content) => {
codeBlocks.push(content)
return `\x00CODE${codeBlocks.length - 1}\x00`
})
// 2단계: 비코드 부분의 Markdown 기호 제거
// ...
// 3단계: 인라인 코드 내용 복원
visualText = visualText.replace(/\x00CODE(\d+)\x00/g, (match, index) => {
return codeBlocks[parseInt(index)]
})소스 코드 위치: index.ts:168-193
6. 경계 상황 처리
플러그인은 다양한 경계 상황을 올바르게 처리할 수 있습니다:
| 시나리오 | 처리 방식 |
|---|---|
| Emoji 이모지 | Bun.stringWidth를 사용하여 올바르게 표시 너비 계산 |
| Unicode 문자 | 중국어, 일본어 등 고정폭 문자 올바르게 정렬 |
| 빈 셀 | 최소 너비(3 문자)까지 공백 채우기 |
| 초과 내용 | 정상적으로 처리, 잘라내지 않음 |
7. 자동 작업
플러그인은 백그라운드에서 자동으로 실행됩니다:
- 로그 출력 없음: 콘솔에 어떤 정보도 출력하지 않음
- 오류로 인한 중단 없음: 포맷팅이 실패하더라도 AI의 정상적인 출력에 영향을 주지 않음
포맷팅 과정에서 오류가 발생하면 플러그인은 원본 텍스트를 유지하고 끝에 HTML 주석을 추가합니다:
<!-- table formatting failed: [오류 정보] -->8. 검증 피드백
플러그인은 테이블 구조가 유효한지 검증합니다. 유효하지 않은 테이블은 포맷팅되지 않고 원본 그대로 유지되며, 힌트가 추가됩니다:
<!-- table not formatted: invalid structure -->유효한 테이블의 요구사항:
- 최소 2행(구분 행 포함)
- 모든 행의 열 수가 일치
- 구분 행이 있어야 함(형식:
|---|---|)
플러그인의 한계
지원되지 않는 시나리오
- HTML 테이블: Markdown 파이프 테이블(
| ... |)만 처리 - 다중 행 셀:
<br>태그가 포함된 셀은 지원하지 않음 - 구분 행 없는 테이블:
|---|---|구분 행이 있어야 함 - 헤더 없는 테이블: 헤더 행이 있어야 함
확인 포인트
이 과정을 완료한 후 다음 질문에 답할 수 있어야 합니다:
- [ ] 플러그인은 어떻게 자동으로 트리거되나요? (답:
experimental.text.complete훅) - [ ] 왜 "숨김 모드 호환"이 필요한가요? (답: 숨김 모드는 Markdown 기호를 숨겨 너비 계산에 영향을 줌)
- [ ] 인라인 코드 안의 Markdown 기호가 제거되나요? (답: 아니요, 코드 내의 Markdown 기호는 완전히 유지됨)
- [ ] 유효하지 않은 테이블은 어떻게 처리되나요? (답: 원본 그대로 유지되며 오류 주석이 추가됨)
이 과정 요약
| 기능 | 설명 |
|---|---|
| 자동 포맷팅 | AI가 텍스트 생성을 완료하면 자동으로 트리거되며, 수동 조작이 필요 없음 |
| 숨김 모드 호환 | Markdown 기호가 숨겨진 후의 표시 너비를 올바르게 계산 |
| 정렬 지원 | 왼쪽 정렬, 가운데 정렬, 오른쪽 정렬 |
| 중첩 Markdown | 다중 라운드 정규식 제거, 중첩 구문 지원 |
| 코드 블록 보호 | 인라인 코드 안의 기호는 원본 그대로 유지 |
| 경계 상황 | Emoji, Unicode, 빈 셀, 초과 내용 |
| 자동 작업 | 로그 없음, 오류로 인한 중단 없음 |
| 검증 피드백 | 유효하지 않은 테이블에 오류 주석 추가 |
다음 과정 예고
다음 과정에서는 **숨김 모드 원리**를 심층적으로 다룹니다.
학습할 내용:
- OpenCode 숨김 모드의 작동 원리
- 플러그인이 표시 너비를 올바르게 계산하는 방법
Bun.stringWidth의 역할
부록: 소스 코드 참조
클릭하여 소스 코드 위치 펼치기
업데이트 시간: 2026-01-26
| 기능 | 파일 경로 | 행 번호 |
|---|---|---|
| 플러그인 진입점 | index.ts | 9-23 |
| 테이블 감지 | index.ts | 58-61 |
| 테이블 검증 | index.ts | 70-88 |
| 너비 계산(숨김 모드) | index.ts | 161-196 |
| 정렬 방식 구문 분석 | index.ts | 141-149 |
| 코드 블록 보호 | index.ts | 168-173 |
핵심 상수:
colWidths[col] = 3: 열 최소 너비는 3 문자(index.ts:115)
핵심 함수:
formatMarkdownTables(): 메인 처리 함수, 텍스트의 모든 테이블 포맷팅getStringWidth(): 문자열 표시 너비 계산, Markdown 기호 제거isValidTable(): 테이블 구조 유효성 검증