두 개의 열린 PR(풀 리퀘스트)은 게시글 원본(raw)에서 BBCode 속성을 편집하는 서로 다른 접근법을 도입합니다:
-
#43187 — 반복 이벤트 후속 토픽 모드 추가는
BbcodeRewriter를 도입하며, 이는 렌더링된 이벤트를 식별하고 자동 롤오버(rollover) 시 해당 BBCode 시작 태그를 안전하게 다시 작성(rewrite)합니다. -
#42932 — 워크플로에 이벤트 작업 추가는
PrettyText.update_bbcode_attributes를 도입하며, 이는 파싱된 BBCode 블록을 찾아 워크플로가 이벤트를 열거나 닫을 때 그 속성을 업데이트합니다.
#43187 리뷰 중 Martin은 이벤트 전용 서비스에서 범용 BBCode 조작 기능을 추출할 것을 제안했습니다.
저는 처음에 다른 PR의 관련 토큰 기반 기능 일부를 포함시켰지만, 이로 인해 #43187 내에 두 가지 재작성 메커니즘이 도입되었습니다. 해당 브랜치에서 토큰 기반 API에는 프로덕션 호출자가 없었으므로, 저는 commit 6d13371에서 이를 제거했습니다.
토큰 기반 API는 여전히 #42932에 대해 독립적으로 유용합니다.
저는 이 두 가지 접근법이 궁극적으로 하나의 재사용 가능한 핵심 API로 수렴해야 하는지, 아니면 서로 다른 요구 사항 때문에 둘 다 유지하는 것이 정당화되는지 궁금했습니다.
현재 구현 비교
아래 표는 어느 쪽 접근법이 본질적으로 잘못되었다고 제안하기보다는 코드가 현재 구현하는 내용을 설명합니다.
| 기능 | BbcodeRewriter (#43187) |
토큰 기반 API (#42932) | 의미 및 중요성 |
|---|---|---|---|
| 1. Discourse의 BBCode 파서 사용 | Markdown 엔진에서 사용하는 것과 동일한 시작 태그 문법을 가진 parseBBCodeTag를 사용하여 후보 시작 태그를 스캔합니다. |
Markdown 토큰을 얻어 일치하는 BBCode 블록을 찾기 위해 DiscourseMarkdownIt.parse를 사용합니다. |
두 접근법 모두 BBCode 구문을 이해하며, 독립적인 정규 표현식에만 의존하지 않습니다. 편집할 태그를 식별하는 방식에서 차이가 있습니다. |
| 2. 대상 BBCode 블록 선택 | 후보 시작 태그를 일시적으로 표시(mark)하고 게시글을 렌더링한 뒤, div.discourse-post-event와 같은 선택된 HTML 요소에 해당하는 후보를 식별합니다. |
요청된 태그 이름과 meta.bbcode가 일치하는 첫 번째 파싱된 BBCode 토큰을 선택합니다. |
대상 동일성이 중요합니다. #43187은 Discourse가 실제로 렌더링한 이벤트와 원본 BBCode를 연관시켜야 합니다. #42932의 워크플로는 첫 번째 일치하는 파싱된 이벤트 블록을 사용합니다. |
| 3. 누락되거나 모호한 대상 처리 | 대상을 신뢰할 수 있게 식별할 수 없을 때 TargetNotFound, AmbiguousTarget 또는 UnsupportedRenderer를 발생시킵니다. |
일치하는 파싱된 블록이 없으면 null을 반환하고, 그렇지 않으면 첫 번째 일치 항목을 선택합니다. |
실패 동작이 다릅니다. #43187은 모호한 자동 편집을 의도적으로 거부합니다. 호출자가 관련 블록이 하나만 있을 것으로 기대하는 경우, 첫 번째 파싱된 블록을 선택하는 것으로 충분할 수 있습니다. |
| 4. 원본-렌더링 매핑 검증 | 표시된 콘텐츠를 렌더링하고 의도된 HTML 요소에 마커가 있는지 확인하며, 마커 삽입 및 삭제가 렌더링된 HTML을 변경하지 않는지 확인합니다. | 추가적인 렌더링된 HTML 동일성 검사 없이 Markdown 파서의 토큰 메타데이터를 사용하여 시작 태그를 찾습니다. | 이는 잘못된 렌더링된 대상을 편집하는 것을 방지합니다. 백그라운드 잡이 기존 이벤트를 자동으로 아카이브할 때 특히 관련이 있지만, 추가적인 렌더링과 복잡성을 수반합니다. |
| 5. 선택된 시작 태그 확인 | 재작성 전에 제공된 원본(raw)을 다시 스캔하여 선택된 후보가 여전히 예상되는 시작 태그 텍스트와 일치하는지 확인합니다. | 별도의 예상 시작 태그 비교 없이 업데이트 호출 중에 일치하는 시작 태그를 찾습니다. | 이는 내부적 일관성 검사입니다. 이전 선택과 더 이상 일치하지 않는 후보를 재작성하는 것을 방지하는 데 도움이 됩니다. 자체적으로는 동시 데이터베이스 편집에 대한 보호 장치가 아닙니다. |
| 6. 속성 이름 및 기존 값 처리 | 파서의 속성 경계를 사용하여 선택된 속성을 재작성합니다. 또한 등가 렌더링 속성 이름을 정규화하여 Recurrence와 recurrence와 같은 변형이 일관되게 제거될 수 있도록 허용합니다. |
속성 토큰 매칭을 사용하여 요청된 이름을 대체하거나 제거하고, 공유 BBCode 속직 직렬화기를 사용하여 값을 삽입합니다. | 둘 다 무관한 속성을 보존하려는 목적을 가집니다. 이벤트 아카이빙 시 등가 이름 정규화는 특히 중요합니다. 재귀(recurrence) 별명을 남겨두면 완료된 이벤트가 여전히 반복적으로 남을 수 있기 때문입니다. |
| 7. 재작성된 태그 검증 | 재작성된 시작 태그를 다시 파싱하여 그 속성이 의도된 값과 일치하는지 확인합니다. 잘못된 왕복(round-trip)은 오류를 발생시킵니다. | 최종 파싱 및 비교 단계 없이 재작성된 시작 태그를 직접 생성합니다. | 왕복 검증을 통해 직렬화 문제를 포착합니다. 예를 들어 변경된 속성 값이나 잘못된 경계 등입니다. 토큰 기반 접근법은 더 단순하지만 이 추가 런타임 검사 대신 재작성 로직과 테스트에 의존합니다. |
| 8. 무관한 게시글 콘텐츠 보존 | 주변 원본 콘텐츠를 유지하면서 선택된 시작 태그만 변경합니다. 또한 무관한 속성 값이 재작성을 견디는지 확인합니다. | 주변 원본 콘텐츠와 수정을 요청받지 않은 속성을 유지하면서 선택된 시작 태그 부분 문자열만 대체합니다. | 둘 다 의도하지 않은 게시글 변경을 피해야 합니다. 이벤트 관련 편집은 주변 토론이나 무관한 BBCode 속성을 조용히 변경해서는 안 됩니다. |
| 9. 구현 비용 | 후보 스캔, 일시적 마커, 게시글 렌더링, HTML 검사, 속성 재작성 및 검증을 필요로 합니다. | Markdown 토큰을 파싱하고 첫 번째 일치하는 시작 태그를 선택하여 직접 재작성합니다. | 더 강력한 런타임 검사는 비용을 수반합니다. 추가적인 안전장치를 모든 호출자에게 제공해야 하는지, 선택적이어야 하는지, 아니면 애플리케이션이 필요로 하는 곳에만 구현해야 하는지가 문제입니다. |
왜 요구 사항이 다른가
#43187의 경우, 후속 롤오버는 한 occurrence(발생)가 끝나면 자동으로 실행됩니다. 원래 이벤트는 비재귀 occurrence으로 아카이브되고, 다음 occurrence을 위한 새 토픽이 생성됩니다.
잘못된 [event] 블록이 수정되거나 재작성된 속성이 의도된 이벤트를 더 이상 대표하지 않을 경우, 오류는 자동 롤오버 시도 사이에 지속될 수 있습니다.
#42932의 경우, 워크플로 작업은 명시적으로 이벤트가 열리거나 닫히도록 요청합니다. 그 현재 토큰 기반 구현은 첫 번째 일치하는 파싱된 이벤트 블록을 수정합니다.
따라서 토큰 기반 접근법은 해당 사용 사례에 부적합하다고 단정할 수는 없습니다. 단순히 다른 선택 및 검증 보장을 제공할 뿐입니다.
두 접근법은 이미 parseBBCodeTag와 serializeBBCodeAttr를 포함한 일부 기본 인프라를 공유합니다.
공유 추상화를 위한 가능한 방향
하나의 가능성은 세 가지 책임을 분리하는 것입니다:
-
BBCode 시작 태그 찾기 — 파서 토큰, 명시적 후보 선택 또는 렌더링된 요소 매핑을 사용하여.
-
그 속성 재작성 — 속성 경계, 직렬화 및 무관한 값 보존을 위한 하나의 공유 구현을 사용하여.
-
결과 검증 — 호출자에 따라 대상 동일성 강제, 모호성 감지, 렌더링된 콘텐츠 동등성 또는 속성 왕복 검사를 선택적으로 적용.
이를 통해 더 단순한 워크플로가 직관적인 토큰 선택을 사용할 수 있고, 더 민감한 자동 편집 작업은 더 강력한 안전장치를 요청할 수 있게 될 것입니다.
그러나 이는 두 개의 더 작은 API를 유지하는 것보다 불필요한 복잡성을 도입할 수도 있습니다.
유지보수자를 위한 질문
서로 다른 대상 선택 요구 사항을 고려할 때, 두 PR이 각자의 별도 재작성 API를 유지하는 것이 바람직할까요?
또는 구성 가능한 대상 선택 및 검증을 가진 공유 저 수준 속성 재작성기가 장기적으로 더 나은 방향일까요?
특히 BbcodeRewriter가 현재 구현하는 추가적인 안전 검사가 일반적으로 유용한 핵심 기능으로 간주되어야 하는지, 아니면 더 강한 보장이 필요한 사용 사례에 특정되어 남아야 하는지에 대해 관심이 많습니다.
저는 어느 쪽 PR이 다른 쪽을 블로킹해야 한다고 제안하는 것이 아닙니다. 목표는 두 기능이 발전함에 따라 Discourse의 핵심 BBCode 인프라에서 불필요한 중복을 피하는 것입니다.