Skip to content

기능 개요: 자동 포맷팅의 마법

학습 후 할 수 있는 것

  • 플러그인의 8가지 핵심 기능 이해
  • 이 플러그인이 적합한 시나리오 파악
  • 플러그인의 한계(할 수 없는 것) 이해

현재 겪고 있는 문제

플러그인 정보

이 플러그인의 전체 이름은 @franlol/opencode-md-table-formatter이며, 이하 "테이블 포맷팅 플러그인"이라고 합니다.

AI가 생성한 Markdown 테이블은 종종 다음과 같습니다:

markdown
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |

열 너비가 고르지 않아 보기 불편합니다. 수동으로 조정하시겠습니까? AI가 새 테이블을 생성할 때마다 매번 조정해야 하니 너무 피곤합니다.

언제 사용해야 하나요

  • AI가 Markdown 테이블을 생성했고 더 깔끔하게 만들고 싶을 때
  • OpenCode의 숨김 모드(Concealment Mode)를 활성화했고 테이블 정렬에 문제가 있을 때
  • 테이블 열 너비를 수동으로 조정하기 귀찮을 때

핵심 아이디어

이 플러그인의 작동 원리는 매우 간단합니다:

AI 텍스트 생성 → 플러그인 테이블 감지 → 구조 검증 → 포맷팅 → 미화된 텍스트 반환

이 플러그인은 OpenCode의 experimental.text.complete 훅에 마운트되어 있어 AI가 텍스트 생성을 완료할 때마다 자동으로 처리됩니다. 수동으로 트리거할 필요가 없으며, 전 과정이 자동으로 진행됩니다.

8가지 핵심 기능

1. 자동 테이블 포맷팅

플러그인은 AI가 생성한 텍스트에서 Markdown 테이블을 자동으로 감지하고 열 너비를 통일하여 테이블을 깔끔하고 아름답게 만듭니다.

포맷팅 전:

markdown
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |

포맷팅 후:

markdown
| 名称         | 描述         | 状态       |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成    |
| API          | 接口文档     | 🚧 进行中  |

트리거 조건

플러그인은 experimental.text.complete 훅에 마운트되어 있어 AI가 텍스트 생성을 완료하면 자동으로 트리거되며, 수동 조작이 필요하지 않습니다.

2. 숨김 모드 호환

OpenCode는 기본적으로 숨김 모드(Concealment Mode)를 활성화하며, Markdown 기호(예: **, *, ~~)를 숨깁니다.

일반적인 테이블 포맷팅 도구는 이 점을 고려하지 않아 너비를 계산할 때 **도 함께 계산하여 정렬이 어긋나는 문제가 발생합니다.

이 플러그인은 숨김 모드에 최적화되어 있습니다:

  • 너비를 계산할 때 **굵게**, *기울임*, ~~취소선~~ 등의 기호를 제거
  • 출력할 때 원본 Markdown 구문 유지
  • 최종 효과: 숨김 모드에서 테이블이 완벽하게 정렬됨
기술 세부사항: 너비 계산 로직
typescript
// Markdown 기호 제거(너비 계산용)
visualText = visualText
  .replace(/\*\*\*(.+?)\*\*\*/g, "$1") // ***굵은기울임*** → 텍스트
  .replace(/\*\*(.+?)\*\*/g, "$1")     // **굵게** → 굵게
  .replace(/\*(.+?)\*/g, "$1")         // *기울임* → 기울임
  .replace(/~~(.+?)~~/g, "$1")         // ~~취소선~~ → 취소선

소스 코드 위치: index.ts:181-185

3. 정렬 지원

Markdown 테이블의 세 가지 정렬 방식을 지원합니다:

구문정렬 방식효과
--- 또는 :---왼쪽 정렬텍스트 왼쪽(두 구문의 효과는 동일)
:---:가운데 정렬텍스트 가운데
---:오른쪽 정렬텍스트 오른쪽

예시:

markdown
| 왼쪽 정렬 | 가운데 정렬 | 오른쪽 정렬 |
|--- | --- | ---|
| 텍스트 | 텍스트 | 텍스트 |

포맷팅 후 각 열은 지정된 방식으로 정렬되며, 구분 행은 정렬 방식에 따라 다시 생성됩니다.

4. 중첩 Markdown 처리

테이블 셀 안에는 ***굵은기울임***과 같은 중첩된 Markdown 구문이 있을 수 있습니다.

플러그인은 다중 라운드 정규식 알고리즘을 사용하여 바깥에서 안으로 순차적으로 제거합니다:

***굵은기울임*** → **굵은기울임** → *굵은기울임* → 굵은기울임

이렇게 하면 여러 레벨이 중첩되어도 너비 계산이 정확합니다.

5. 코드 블록 보호

인라인 코드(백틱으로 감싸진) 안의 Markdown 기호는 원본 그대로 유지되어야 하며, 제거되지 않아야 합니다.

예를 들어 `**bold**`의 경우 사용자가 보는 것은 **bold**라는 8개의 문자이지 bold라는 4개의 문자가 아닙니다.

플러그인은 먼저 코드 블록 내용을 추출하고, 다른 부분의 Markdown 기호를 제거한 후 코드 블록 내용을 다시 넣습니다.

기술 세부사항: 코드 블록 보호 로직
typescript
// 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 주석을 추가합니다:

markdown
<!-- table formatting failed: [오류 정보] -->

8. 검증 피드백

플러그인은 테이블 구조가 유효한지 검증합니다. 유효하지 않은 테이블은 포맷팅되지 않고 원본 그대로 유지되며, 힌트가 추가됩니다:

markdown
<!-- 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.ts9-23
테이블 감지index.ts58-61
테이블 검증index.ts70-88
너비 계산(숨김 모드)index.ts161-196
정렬 방식 구문 분석index.ts141-149
코드 블록 보호index.ts168-173

핵심 상수:

  • colWidths[col] = 3: 열 최소 너비는 3 문자(index.ts:115)

핵심 함수:

  • formatMarkdownTables(): 메인 처리 함수, 텍스트의 모든 테이블 포맷팅
  • getStringWidth(): 문자열 표시 너비 계산, Markdown 기호 제거
  • isValidTable(): 테이블 구조 유효성 검증