Releases: hkjang/muni
Release list
muni v0.38.0
What's Changed
Full Changelog: v0.37.0...v0.38.0
muni v0.37.0
화면을 쓰는 사람과 그 화면을 띄워 놓고 지키는 사람을 위한 사용자 가이드와 관리자 가이드가 생겼습니다. 실린 그림 스무 장은 모두 실제로 띄운 muni 에서 찍은 것입니다.
추가
사용자 가이드와 관리자 가이드
muni 에는 README 와 운영 안내만 있었습니다. 설치하는 사람이 볼 글은 있었지만, 로그인해서 문서를 쓰고 댓글을 달고 내보내는 사람이 볼 글도, 계정을 만들고 사고가 났을 때 무엇을 확인할지 적은 글도 없었고 — 화면 캡처는 한 장도 없었습니다.
사용자 가이드(PDF)는 로그인부터 홈·워크스페이스·편집기·댓글·버전·내보내기·공유·검색·빠른 이동·개인 설정까지를, 관리자 가이드(PDF)는 구성 요소·설치·설정·계정과 권한·운영·장애 대응·보안을 다룹니다. README 에서 두 문서로 가는 길을 냈습니다.
문서에 적은 것은 코드에서 읽었습니다. 환경 변수 표는 internal/config 에서, API 의 메서드와 경로는 라우트가 등록된 자리에서, 오류 메시지는 서버가 실제로 내는 문구에서 가져왔습니다. 겹치는 자리는 옮겨 적지 않고 링크합니다 — 백업·복구·키 교체·퇴사자 정리는 운영 안내가 정본이고, 관리자 가이드는 그것을 가리키며, 운영 안내는 관리자 가이드를 되가리킵니다. 정본은 하나입니다.
화면 캡처 스크립트
그림은 손으로 찍지 않았습니다. frontend/scripts/guide-screenshots.mjs(npm run screenshots)가 데모 데이터 — 회사 하나, 팀 하나, 문서 다섯, 사람 셋 — 를 채우고 1440x900 headless Chromium 으로 사용자 화면 열두 장과 관리 화면 여덟 장을 찍습니다. 화면이 바뀌면 다시 돌려 다시 찍습니다.
데모 데이터를 만드는 스크립트이므로 조심스럽게 두었습니다. 대상 주소는 e2e 와 공유하지 않는 전용 변수(MUNI_GUIDE_BASE_URL)로만 받고 로컬이 아니면 멈춥니다. 자격 증명은 환경 변수로만 받고, 데모 계정의 비밀번호는 실행할 때마다 새로 만들어 어디에도 적히지 않습니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다. 이번 릴리스는 문서만 더했고 서버와 화면의 동작은 v0.36.0 과 같습니다.
gzip -dc muni-v0.37.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -d오프라인 설치
릴리스 asset 에는 muni:v0.37.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.37.0.tar.gz | docker load
docker image inspect muni:v0.37.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.37.0.tar.gz - 크기: (릴리스 후 기록)
- SHA-256: (릴리스 후 기록)
- 내부 이미지 태그:
muni:v0.37.0
sha256sum muni-v0.37.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.36.0
한글 문서에 없는 것 셋 — 인용문, 코드 블록, 구분선 — 을 한글이 쓰는 방법으로 적고, 그 방법대로 다시 읽습니다. 두 번 오간 문서에서 사라지던 자리들입니다.
추가
인용문과 코드 블록 (.hwpx)
한글에는 인용문도 코드 블록도 없습니다. 쓰는 쪽은 인용을 들여쓴 문단으로, 코드를 고정폭 문단 여러 개로 흘려보내고 있었고 — 읽는 쪽에는 그것을 되살릴 단서가 없었습니다. 들여쓰고 고정폭으로 그린 문단은 읽는 쪽에 그저 문단입니다.
그래서 .docx 쓰기가 하는 것과 같이 머리에 스타일 둘을 심고(인용/Quote, 코드/Code) 문단이 그것을 번호로 가리킵니다. 스타일 이름을 무엇으로 읽을지는 한 곳에 두어 워드에서 옮겨온 이름(Source Code 따위)도 함께 받습니다.
코드 블록은 한 문단 안에 줄바꿈으로 넣습니다. 줄마다 문단이면 끝을 알릴 것이 없어 나란한 코드 블록 둘이 하나로 돌아옵니다. 인용문은 목록처럼 시작도 끝도 표시가 없으므로 이어지는 줄을 하나로 모읍니다. 인용이 여백에서 한 칸 물러난 것은 인용을 그리는 방법이지 글쓴이가 준 들여쓰기가 아니므로, 읽을 때 덜어냅니다 — 그러지 않으면 왕복마다 한 칸씩 밀립니다.
구분선 (.hwp 읽기, .hwpx 양방향)
구분선은 muni 가 .docx·HTML·마크다운에서 예전부터 주고받던 블록인데, 한글 두 형식은 읽는 쪽이 아예 없었고 쓰는 쪽은 ─ 서른 개를 글자로 적고 있었습니다. 글자는 글자로 돌아오므로, 두 번 오간 문서에는 선이 있던 자리에 그 문단이 그대로 남았습니다.
한글에도 구분선은 없습니다. 한글이 하이픈 한 줄을 그리는 방법 — 아무것도 담지 않은 문단 아래에 선 하나를 긋는 것 — 이 곧 muni 가 쓰는 방법입니다. 선은 문단에 있지 않고 문단은 머리의 borderFill 을 번호로 가리키기만 하며(칸 음영과 같은 간접입니다. .hwp 는 PARA_SHAPE 의 32번째 바이트, .hwpx 는 paraPr 의 <hh:border>), 네 면에 두른 선은 글을 감싼 상자이지 구분선이 아니므로 위나 아래에만 그은 것을 읽습니다. 선이 그어진 문단이 글자를 지녔거나 표를 매달고 있으면 그 선은 그것을 그리는 방법이므로 구분선으로 세지 않습니다.
무엇이 구분선인지는 한 곳의 판단이라 두 리더와 쓰는 쪽이 어긋나지 않습니다.
그 밖에
gofmt 과 go vet 을 이제 손이 아니라 CI 가 돌립니다. 여덟 회차 내리 손으로 돌린 검사입니다. 습관에 들어 있는 검사는 무언가를 잡아낼 바로 그 한 번에 빠집니다. 이제 모든 푸시와 PR 에서 gofmt -l 과 go vet ./... 이 함께 돕니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.36.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -d인용문이나 코드 블록, 구분선이 든 문서를 한글로 내보내셨다면 다시 내보내 보세요. 예전에 내보낸 .hwpx 를 다시 가져오면 인용은 들여쓴 문단으로, 구분선은 ─ 가 늘어선 문단으로 들어옵니다.
오프라인 설치
릴리스 asset 에는 muni:v0.36.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.36.0.tar.gz | docker load
docker image inspect muni:v0.36.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.36.0.tar.gz - 크기: (릴리스 후 기록)
- SHA-256: (릴리스 후 기록)
- 내부 이미지 태그:
muni:v0.36.0
sha256sum muni-v0.36.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.35.0
PDF 에서 첨자가 들어옵니다 — 각주 번호, ㎡ 의 2, H₂O 의 2. 그리고 남이 만든 파일이 메모리로 얼마나 커질 수 있는지에 마지막 한계를 두었습니다.
추가
첨자와 아래첨자
PDF 에는 첨자라는 것이 없습니다. 각주 번호도, ㎡ 의 2 도, H₂O 의 2 도 모두 보통 글자로 그려집니다 — 조금 작은 글꼴로, 나머지 글이 앉은 줄에서 살짝 비켜서. 그것을 평평하게 읽으면 3㎡ 가 32 가 되고, 문장마다 붙은 각주 번호가 낱말 사이에 떨어진 숫자가 됩니다.
이제 줄마다 대부분의 글자가 앉은 자리와 크기를 먼저 구하고 — 짧게 올려 쓴 조각이 그 기준을 끌고 가지 못하도록 — 그보다 작으면서 위로 비켜난 조각을 첨자로, 아래로 비켜난 조각을 아래첨자로 읽습니다. 같은 크기로 높이 있는 것은 첨자가 아니라 다른 줄입니다.
.hwp·.hwpx·DOCX 는 예전부터 첨자를 읽었고, 이제 네 형식이 같습니다.
막은 것
조각 수
바이트는 이미 막고 있었지만, 트리는 글자보다 몇 배를 차지합니다. 빈 태그만 든 64MB 짜리 부품은 작은 업로드로 큰 메모리를 부르는 길이었습니다. 이제 .docx 와 .hwpx 는 부품 하나가 백만 조각을 넘지 못하고, .hwp 는 스트림 하나가 이백만 레코드를 넘지 못합니다. 실제 문서는 여기 근처에도 오지 않습니다 — 오백 쪽짜리 보고서가 이십만 조각쯤입니다.
이것으로 지난 릴리스의 감사에서 열어 두었던 항목이 닫혔습니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.35.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -d각주나 단위가 든 PDF 를 가져오셨다면 다시 가져와 보세요. 각주 번호와 ㎡ 의 2 가 본문에 섞인 숫자로 들어와 있을 수 있습니다.
오프라인 설치
릴리스 asset 에는 muni:v0.35.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.35.0.tar.gz | docker load
docker image inspect muni:v0.35.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.35.0.tar.gz - 크기:
372,460,072 bytes(약 355.2 MiB) - SHA-256:
f83b8bccce6622d8208b1c0ca58c0ec0c70e21e208d6c74f8345493bce1422d3 - 내부 이미지 태그:
muni:v0.35.0
sha256sum muni-v0.35.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.34.0
PDF 를 가져올 때 하이퍼링크가 들어옵니다. 그리고 굵기·기울임이 줄 전체가 아니라 그려진 조각마다 붙습니다.
추가
하이퍼링크
PDF 는 링크의 글자에 표시를 달지 않습니다. 글자는 보통 글자로 그리고, 그 위에 사각형을 하나 얹어 주소를 그 사각형에 적어 둡니다. 둘을 다시 잇는 일은 읽는 쪽의 몫이고, muni 는 그 일을 하지 않아 링크를 통째로 버리고 있었습니다.
이제 쪽마다 링크 주석을 읽어 사각형을 글자와 같은 좌표로 옮기고, 글자 뭉치의 가운데가 사각형 안에 들어올 때 그 주소를 붙입니다. 가운데를 보는 이유는 링크가 밑줄이 끝나는 곳에서 끝나기 때문입니다 — 바로 옆 낱말은 링크가 아닙니다. 문서 안의 다른 자리를 가리키는 링크는 문서가 다른 곳으로 옮겨지면 갈 곳이 없으므로 두지 않습니다. 주소는 다른 네 형식과 같은 판단을 거칩니다 — 열리는 것만 남고 실행되는 것은 주소가 아닙니다.
줄 안의 굵기와 기울임
PDF 의 한 줄은 여러 번에 나누어 그려지고, 그 조각들이 다 같은 모습은 아닙니다. muni 는 줄을 문자열 하나로 들고 있어서 줄 전체가 굵거나 전혀 굵지 않거나 둘 중 하나였고, 기울임은 아예 버려졌으며, 링크는 붙일 자리조차 없었습니다.
이제 줄은 그려진 조각들을 지닌 채로 흐릅니다. 줄이 무엇인지 가리는 일 — 제목인지, 글머리표인지, 표의 행인지 — 은 예전처럼 글자만 보고 하고, 조각은 그 줄이 문서의 글이 되는 마지막 순간에만 쓰입니다. 문단이 다음 줄로 이어질 때도, 글머리표 뒤의 글을 떼어낼 때도 조각은 따라갑니다.
이 문장에는 굵은 낱말 이 가운데 있습니다 가 이제 가운데 세 글자만 굵게 들어옵니다.
정리
링크 주소를 판단하는 자리가 하나로 모였습니다(richdoc.SafeLink). PDF·.hwp·.hwpx·DOCX·HTML·마크다운이 같은 답을 냅니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.34.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -dPDF 를 가져오셨다면 다시 가져와 보세요. 하이퍼링크는 이번에 처음 들어오고, 문장 가운데 굵거나 기울인 낱말도 이제 그 자리에 남습니다.
오프라인 설치
릴리스 asset 에는 muni:v0.34.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.34.0.tar.gz | docker load
docker image inspect muni:v0.34.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.34.0.tar.gz - 크기:
372,451,276 bytes(약 355.2 MiB) - SHA-256:
594ff9e4cecfba8d8b970f7a7decd407120650e116122f73045edc0ab1fe20a8 - 내부 이미지 태그:
muni:v0.34.0
sha256sum muni-v0.34.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.33.0
가져오기가 남의 파일을 읽는 일이라는 점을 놓고, 공개된 자료에 적힌 함정들을 하나씩 muni 코드와 대조했습니다. 실제로 틀린 곳 넷과 막혀 있지 않던 곳 다섯을 고쳤습니다.
틀렸던 것
PDF — 낱말 간격을 한글에도 더하고 있었습니다
형식은 이 점을 분명히 못박습니다. Tw 로 정한 낱말 간격은 한 바이트로 쓰인 32번 코드에만 붙고, "여러 바이트 코드 안에 들어 있는 32라는 값에는 붙지 않는다". muni 는 빈칸으로 읽히는 모든 코드에 붙였습니다. 한글 문서의 코드는 두 바이트이므로, 양쪽 정렬된 문서마다 붓이 종이보다 멀리 나아갔고 — 낱말과 표의 열을 가르는 것이 바로 그 간격입니다.
PDF — 합자가 검색되지 않았습니다
조판가는 fi 를 글리프 하나로 찍습니다. 그것을 그대로 두면 문서에서 "file" 을 찾아도 "file" 이 걸리지 않습니다. 이제 합자는 그것이 대신하는 글자들로 풀고, 조판을 안내할 뿐인 표시 — 소프트 하이픈, 폭 없는 공백, 바이트 순서 표시 — 는 빼고 읽습니다.
한글 두 형식 — 링크 주소를 검사하지 않았습니다
Word·HTML·마크다운 가져오기는 예전부터 주소의 방식을 확인해 http·https·mailto 만 남겼습니다. .hwp 와 .hwpx 만 확인 없이 저장했습니다. 파일이 javascript\:… 를 주소라고 적어 두면 그대로 문서에 들어갔습니다. 화면에 그릴 때 한 번 더 걸러지기는 했지만, 걸러지지 않은 것이 저장되어 있다는 사실은 그대로였습니다. 이제 두 형식이 같은 판단을 씁니다 — 같은 코드로.
DOCX — 끼워 넣은 문서가 통째로 빠졌습니다
Word 에서 파일을 문서에 끼워 넣으면 합쳐지지 않습니다. Word 는 그 파일을 통째로 부품으로 넣고 <w:altChunk> 하나만 남겨 둡니다. 그것을 따라가지 않으면 끼워 넣은 파일이 말한 것이 아무 말도 없이 사라집니다. 이제 따라 읽습니다. 자기 자신을 끼워 넣은 파일이 끝없이 열리지 않도록 깊이와 개수를 막았습니다.
막혀 있지 않던 것
압축 파일과 XML 은 남이 만든 것이고, 파일이 자기에 대해 하는 말은 사실이 아닐 수 있습니다.
- 압축 폭탄. 부품 하나하나의 크기는 막고 있었지만 몇 개인지와 합이 얼마인지는 보지 않았습니다. deflate 는 천 배까지 부풀므로, 20MB 짜리 업로드가 20GB 를 담고 있다고 적어 둘 수 있습니다. 이제 압축을 풀기 전에 디렉터리가 말하는 개수와 합을 봅니다.
- DOCX 의 중첩 깊이.
.hwpx는 512단계에서 막고 있었고.docx는 막고 있지 않았습니다. 트리는 재귀 없이 세우지만 그 뒤로 읽는 코드가 모두 재귀이고, Go 는 스택이 바닥나면 요청이 실패하는 것이 아니라 프로세스가 죽습니다. - DOCX 의 그림 총량. 그림 한 장의 크기는 막혀 있었지만 문서 전체의 합은 막혀 있지 않았습니다.
- 문서 자체의 깊이와 조각 수. 저장 전 검사는 직렬화한 바이트 수(10MB)만 보았습니다. 여는 괄호만 십만 개 든 1MB 짜리는 작고 깊습니다.
- 가져온 그림의 개수와 총량. 파일이 시키는 대로 첨부를 만들고 있었습니다.
대조하고 이미 되어 있던 것
찾아본 것 가운데 muni 가 이미 지키고 있던 것들입니다. 적어 둡니다 — 확인했다는 뜻입니다.
- XXE·억만 웃음(billion laughs). Go 의
encoding/xml은 DTD 실체를 아예 확장하지 않으므로 닿지 않습니다. - SSRF. 어느 가져오기도 주소를 가져오지 않습니다. Word 의 외부 관계는 명시적으로 따라가지 않고, 마크다운·HTML 의 그림은
data:image/*와 이 서버의 첨부만 받습니다. - zip slip. 디스크에 쓰는 것이 없습니다. 부품은 정해진 이름으로만 찾습니다.
- HTML 가져오기. 태그를 지우는 것이 아니라 아는 것만 옮겨 담습니다.
script·style·iframe은 통째로 버리고, 속성은 이름을 하나씩 지정해 읽으므로onclick이 닿을 길이 없습니다. - 깊이 제한. PDF 의 XObject·페이지 트리·객체 파서, HTML, 마크다운은 이미 막혀 있었습니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.33.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -dWord 문서에 다른 파일을 끼워 넣어 두셨다면 다시 가져와 보세요. 그 내용은 지금까지 통째로 빠져 있었습니다.
오프라인 설치
릴리스 asset 에는 muni:v0.33.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.33.0.tar.gz | docker load
docker image inspect muni:v0.33.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.33.0.tar.gz - 크기:
372,448,454 bytes(약 355.2 MiB) - SHA-256:
91cddc68c3f8dbec1577e62ab73483cee210b830d5fe94535de66e98cd78259b - 내부 이미지 태그:
muni:v0.33.0
sha256sum muni-v0.33.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.32.0
PDF 를 가져올 때 쪽마다 되풀이되는 머리말·꼬리말과 쪽 번호가 본문에 섞이던 것과, 쪽을 넘어가는 표가 토막나던 것을 고쳤습니다.
고친 것
종이의 것과 문서의 것
브라우저로 인쇄한 PDF 는 쪽 위에 날짜와 문서 제목을, 쪽 아래에 주소와 1/2 를 찍습니다. 워드나 한글로 만든 보고서도 쪽마다 같은 머리말과 쪽 번호를 답니다. 그것들은 종이에 인쇄된 것이지 문서가 말하는 내용이 아닌데, muni 는 쪽 번호만, 그것도 여백 6% 안에 있고 줄 전체가 숫자일 때만 버렸습니다. 그래서 "9/7/26, 4:52 PM 2026년 사업 계획 보고서" 같은 줄이 쪽마다 하나씩 문단으로 들어왔습니다.
이제 세 가지가 모두 맞을 때 버립니다. 위아래 여백에 있고, 본문 덩어리에서 떨어져 있고 — 그래야 쪽의 첫 줄이 아니라 여백에 적힌 것입니다 — 그리고 머리말답게 생겼을 때. 마지막 조건은 넷 중 하나입니다: 같은 줄이 다른 쪽에도 있거나(숫자만 다른 것은 같은 줄로 봅니다. 1/2 와 2/2 는 같은 꼬리말입니다), 쪽 번호이거나, 주소를 담고 있거나, 본문보다 작은 글씨이거나.
쪽을 넘어가는 표
긴 표는 한 쪽 아래에서 끝나고 다음 쪽 위에서 다시 시작합니다. muni 는 그것을 표 두 개로 만들었고, 그 사이에는 방금 이야기한 머리말·꼬리말이 문단으로 끼어 있었습니다. 표 안에 숫자와 글이 뒤섞여 보이던 이유입니다.
이제 한 쪽의 마지막이 표이고 다음 쪽의 처음이 표이며 두 표가 같은 열 위에 서 있으면 하나로 잇습니다. 새 쪽 머리에 되풀이된 머리글 행은 빼고 이어 붙입니다. 60행짜리 표를 두 쪽에 걸쳐 인쇄한 문서가 31행·30행 두 개가 아니라 61행 하나로 들어옵니다.
표 위에 떠 있던 머리글
표의 머리글은 열 가운데에 놓이고 그 아래 이름은 왼쪽, 숫자는 오른쪽에 붙습니다. 폭이 겹치지 않아 머리글 행이 표에 들지 못하고 표 위의 외톨이 문단으로 남았습니다. 이제 표 바로 위의 줄이 표의 열 수와 똑같이 나뉘면 그 표의 머리글로 봅니다. 열은 정렬이 확인된 행에서만 잡으므로, 가운데 놓인 머리글이 열을 새로 만들지 않습니다.
표 위의 한 줄이 목록이 되던 것
들여쓴 줄이 이어지면 목록으로 봅니다. 그런데 표의 행도 들여써져 있어서, 표 바로 위의 한 줄이 표의 첫 행과 짝을 이뤄 목록으로 읽혔습니다. 이제 목록은 표에 속하지 않은 줄들 사이에서만 찾습니다.
그 밖에
pdfx.PageTexts 를 더했습니다 — 가져오기가 걷어내기 전, 종이에 실제로 그려진 쪽별 글입니다. muni 자신의 PDF 내보내기가 쪽 번호를 제대로 찍는지 확인하는 검사가 이것을 씁니다. 가져오기는 그 쪽 번호를 버리고, 검사는 양쪽을 다 확인합니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.32.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -dPDF 를 가져오셨다면 다시 가져와 보세요. 쪽마다 되풀이되던 머리말·꼬리말과 쪽 번호가 문단으로 남아 있거나, 표가 쪽마다 끊겨 있을 수 있습니다. 그 손실은 가져오던 시점에 일어났으므로 이미 만들어진 문서에서는 되돌아오지 않지만, 원본으로 다시 가져오면 이번에는 제대로 들어옵니다.
오프라인 설치
릴리스 asset 에는 muni:v0.32.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.32.0.tar.gz | docker load
docker image inspect muni:v0.32.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.32.0.tar.gz - 크기:
372,433,359 bytes(약 355.2 MiB) - SHA-256:
c12b33c0a5494eaa00264732fc31578e3fc833fac2baff6e66e31e6007473bc0 - 내부 이미지 태그:
muni:v0.32.0
sha256sum muni-v0.32.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.31.0
PDF 가져오기에서 띄어쓰기가 $ 로 나오던 것과 표가 빈 열로 갈라지던 것을 고쳤습니다. 문서 제목에 한글을 쓸 때 글자가 사라지던 것도, 홈 화면에서 파일을 끌어다 놓을 수 없던 것도 함께 고쳤습니다.
고친 것
PDF — 띄어쓰기가 $ 로 나왔습니다
부분 내장 글꼴은 글자를 글리프 번호로 가리키고, 그 번호가 무엇을 뜻하는지는 만든 프로그램이 /ToUnicode 에 적어 주기로 되어 있습니다. 적지 못하는 글리프를 만나면 번호를 그대로 유니코드라고 적습니다. 브라우저가 인쇄한 문서에서 띄어쓰기 글리프가 1번이면 <0001> <0001> 이라고 적히고, 띄어쓰기가 36번인 글꼴이면 <0024> <0024> — 즉 $ 라고 적힙니다. muni 는 그 말을 믿었습니다.
이제 내장 글꼴 자신의 문자표(TrueType·OpenType cmap)를 읽습니다. 글꼴은 36번 글리프가 띄어쓰기라고 말하고, 그것이 사실입니다. 제어 문자로 적힌 것은 애초에 글이 아니므로 버립니다. 만든 프로그램이 /ActualText 로 직접 바로잡아 둔 경우에는 그 말을 먼저 따릅니다.
실제 한글 PDF 한 장에서 사라졌던 띄어쓰기 마흔다섯 개가 제자리로 돌아왔습니다. "이문서는띄어쓰기가여러번들어간문장입니다" 가 "이 문서는 띄어쓰기가 여러 번 들어간 문장입니다" 가 됩니다.
PDF — 표가 빈 열로 갈라졌습니다
열을 칸의 왼쪽 끝으로 묶고 있었습니다. 그런데 보고서의 표는 머리글을 가운데로, 이름을 왼쪽으로, 숫자를 오른쪽으로 놓습니다 — 같은 열인데 왼쪽 끝이 저마다 다릅니다. 그래서 정렬 방식마다 열이 하나씩 생겨 네 열짜리 표가 일곱 열로, 그중 셋은 빈 칸으로 들어왔습니다.
이제 칸이 차지한 폭이 겹치는지로 묶습니다. 가운데 놓인 머리글과 그 아래 왼쪽으로 붙은 이름은 폭이 겹치므로 한 열입니다. 격자는 칸이 가장 많은 행에서 가져와서, 표 전체를 가로지르는 제목 칸이 열을 모두 하나로 삼켜 버리지 않습니다.
문서 제목 — 한글이 잘 써지지 않았습니다
제목 칸은 글자를 칠 때마다 문서 캐시를 고쳤고, 그때마다 편집기 화면 전체가 다시 그려졌습니다. 한글은 ㅎㅏㄴ 을 모아 한 을 만드는 동안 입력기가 조합 상태를 들고 있는데, 그 밑에서 값이 통째로 갈리면 조합이 깨집니다. 게다가 자동 저장은 편집 1.5초 뒤에 서버가 아는 문서 전체를 같은 캐시에 되돌려 놓아서, 쓰고 있던 제목을 이미 저장된 제목으로 덮었습니다.
이제 제목 칸은 쓰는 동안 글자를 자기 안에만 두고, 칸을 벗어나거나 Enter 를 누를 때 한 번 저장합니다. 자동 저장이 도중에 끼어들어도 쓰던 제목은 그대로입니다. 조합 중에는 저장하지 않습니다.
한글 조합 중의 Enter
Enter 는 한글에서 음절을 확정하는 키이기도 합니다. 그런데 폴더 이름 바꾸기, 새 문서 만들기, 판 이름, 찾기·바꾸기, 언급, 빠른 이동, 슬래시 메뉴, 링크·이미지 입력이 모두 그 Enter 를 명령으로 받아, 마지막 글자가 반쯤 쓰인 채로 실행되었습니다. 이제 조합 중인 Enter 는 조합을 끝낼 뿐입니다.
추가
홈 화면에서도 끌어다 놓기
지금까지 목록 화면에 파일을 떨어뜨리면 브라우저가 그 파일을 열며 보던 화면을 떠났습니다. 이제 홈과 워크스페이스 어디에 놓아도 가져오기 창이 그 파일을 든 채로 열립니다. 워크스페이스에서는 보고 있는 폴더가 이미 골라져 있고, 여러 개를 놓으면 파일마다 문서 하나씩 차례로 만듭니다. 놓을 수 없는 화면(휴지통, 읽기 전용 워크스페이스)에서도 브라우저가 파일을 열지는 않습니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.31.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -dPDF 를 가져오셨다면 다시 가져와 보세요. 띄어쓰기가 $ 나 빈 곳으로 바뀌어 있거나, 표에 빈 열이 끼어 있을 수 있습니다. 그 손실은 가져오던 시점에 일어났으므로 이미 만들어진 문서에서는 되돌아오지 않지만, 원본으로 다시 가져오면 이번에는 제대로 들어옵니다.
오프라인 설치
릴리스 asset 에는 muni:v0.31.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.31.0.tar.gz | docker load
docker image inspect muni:v0.31.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.31.0.tar.gz - 크기:
372,429,961 bytes(약 355.2 MiB) - SHA-256:
d08c731c8e17131c46c764da037683c79193f555f9cfa02b6840a9f497aacf3d - 내부 이미지 태그:
muni:v0.31.0
sha256sum muni-v0.31.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.30.0
편집 중인 문서에 파일을 끌어다 놓으면 그 자리에 내용이 들어갑니다. PDF·DOCX·HWP·HWPX·Markdown·TXT·HTML 과 그림 — 새 문서를 만들 때 가져오던 그 형식들이, 이제 쓰고 있는 문서 한가운데로도 들어옵니다.
추가
끌어다 놓기
지금까지 문서 파일은 새 문서를 만들 때만 가져올 수 있었고, 편집기에 파일을 떨어뜨리면 브라우저가 그 파일을 열며 편집 중인 문서를 떠났습니다. 이제:
- 놓은 자리에 들어갑니다. 문단 위에 놓으면 그 문단 뒤에 — 가리키던 글자에서 문단을 쪼개지 않습니다 — 여백에 놓으면 문서 끝에. 끌고 있는 동안 어디에 놓이는지 화면이 말합니다.
- 한 번의 편집입니다. 파일 하나의 내용이 한 트랜잭션으로 들어가서 실행 취소 한 번에 통째로 되돌아갑니다. 함께 편집 중인 사람에게도 같은 경로로 전해집니다.
- 여러 파일은 차례대로. 놓은 순서대로 하나씩, 앞 파일이 들어간 뒤에 다음 파일이 들어갑니다. 읽지 못한 파일은 이름과 이유를 알리고 나머지는 계속합니다. 지원하지 않는 파일은 건너뛰고 셉니다.
- 그림은 이미지로. 문서 파일 안의 그림은 그 문서의 첨부가 되어 저장·내보내기 경로를 그대로 탑니다. 그림 파일 자체를 놓으면 이미지로 들어갑니다.
- 빈 문서에 놓으면 파일이 그 문서가 됩니다. 빈 문단을 남기지 않고, 파일이 지닌 제목·머리말·꼬리말·용지 방향 — 문서에 아직 없는 것만 — 을 문서가 이어받습니다.
- 같은 길이 셋입니다. 끌어다 놓기, 클립보드의 파일 붙여 넣기(화면 캡처 포함), 슬래시 메뉴의
/파일. - 읽기 전용에서는 놓아도 아무 일도 하지 않되, 브라우저가 파일을 여는 일도 없습니다. 문서 밖 어디에 떨어뜨려도 같습니다.
API
POST /api/v1/documents/{id}/import — 파일을 읽어 내용을 돌려줍니다. 그림은 그 문서의 첨부가 되고, 문서 자체는 편집기가 저장하기 전까지 바뀌지 않습니다. 문서 EDITOR 권한이 필요합니다. 새 문서를 만드는 POST /api/v1/import 와 파서를 함께 씁니다 — 형식별 파서를 고르는 자리가 하나로 모였습니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.30.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -d오프라인 설치
릴리스 asset 에는 muni:v0.30.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.30.0.tar.gz | docker load
docker image inspect muni:v0.30.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.30.0.tar.gz - 크기:
372,427,238 bytes(약 355.2 MiB) - SHA-256:
f198e83836f5ad0d2f6f8a38d67e4af38666fe99d797608162c45f754c78d959 - 내부 이미지 태그:
muni:v0.30.0
sha256sum muni-v0.30.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.
문서
muni v0.29.0
실제 정부 문서의 머리글 그림 — 1632픽셀짜리를 163픽셀로 줄여 그린 것입니다. 그림의 바이트는 원본 크기 그대로이고 문서는 그것을 줄여서 그리는데, 한글 두 리더가 그린 크기를 버려서 그 머리글 그림이 쪽을 가득 채운 채로 들어왔습니다. .docx 리더는 예전부터 <wp:extent> 를 지켰고 .hwpx 쓰기도 width 를 읽고 있었는데, 한글 쪽 입구만 비어 있었습니다.
추가
크기는 그림 자신에게 없습니다
.hwp 는 그림 위의 SHAPE_COMPONENT 에 크기를 두 개 적고 그 둘째가 그려진 크기 입니다. 그 낱말이 어디서 시작하는지는 컴포넌트가 컨트롤 바로 아래인지(컨트롤 아이디를 두 번 적습니다) 묶음 안인지(한 번)에 따라 다르니, 한 자리만 보고 세면 다른 문서에서 어긋납니다. .hwpx 는 <hp:sz> 가 그려진 크기이고 <hp:orgSz> 가 원본입니다.
무엇을 크기로 볼지는 한 곳에 있습니다
hangul.PictureSize 한 곳에 두어 두 리더가 함께 씁니다. 가로세로 둘 다 실제 길이일 때만 기록합니다 — 반쪽짜리 크기는 없느니만 못합니다.
.hwpx 쓰기는 높이도 적힌 대로 씁니다
바이트에서 다시 계산하면, 일부러 눌러 놓은 그림의 모양이 왕복에서 풀립니다.
고침
.hwp — 그림이 이웃의 그림으로 들어오던 것
그림이 적어 둔 번호는 스트림의 번호가 아니라 DocInfo 의 BIN_DATA 레코드를 세는 번호 이고, 어느 스트림인지는 그 레코드가 말합니다. 그림 셋을 쓴 차례와 적어 둔 차례가 다른 실제 보고서(basicsReport.hwp)에서는 세 그림이 모두 이웃의 그림으로 들어왔습니다.
그린 크기를 읽게 되면서 드러났습니다 — 그림마다 그려진 크기가 자기 그림이 아니라 다른 그림의 생김새와 꼭 맞았고, 셋이 한 바퀴 도는 모양이었습니다. 레코드가 그 번호에 대해 아무 말도 하지 않는 파일은 예전처럼 번호를 스트림의 것으로 읽습니다. 두 차례가 같은 파일은 늘 그래 왔습니다.
확인
변경 전에 실패하는 것을 먼저 확인한 새 테스트 넷입니다.
.hwp가 SHAPE_COMPONENT 의 둘째 크기를,.hwpx가<hp:sz>를 읽는지- 눌러 놓은 그림이
.hwpx왕복에서 가로세로 그대로인지 - 그림이 BIN_DATA 레코드가 가리키는 스트림과 짝지어지는지
공개된 실제 파일 스물둘 — .hwp 열여섯과 .hwpx 여섯 — 의 Corpus·RealRoundTrip 이 통과합니다. 고친 뒤에는 basicsReport.hwp 의 세 그림 모두 그려진 크기와 원본 크기의 비율이 같습니다.
gofmt -l, go vet ./..., go test ./... 전체가 통과합니다.
업그레이드
마이그레이션과 설정 변경은 필요하지 않습니다.
gzip -dc muni-v0.29.0.tar.gz | docker load
docker compose -f compose.example.yaml --env-file .env up -d그림이 든 한글 문서를 가져오셨다면 다시 가져와 보세요. 줄여 그린 그림은 이번에 처음 그 크기대로 들어오고, 그림이 여럿인 옛 문서는 짝이 바로잡힙니다. 이미 가져와 둔 문서는 그대로 남습니다.
오프라인 설치
릴리스 asset 에는 muni:v0.29.0 Docker 이미지가 포함되어 있습니다.
gzip -dc muni-v0.29.0.tar.gz | docker load
docker image inspect muni:v0.29.0
cp .env.example .env
# .env의 네 값을 운영 환경에 맞게 변경합니다.
docker compose -f compose.example.yaml --env-file .env up -d애플리케이션이 반드시 필요로 하는 런타임 환경변수는 다음 네 개입니다.
| 환경변수 | 설명 |
|---|---|
POSTGRES_DSN |
PostgreSQL 접속 문자열 |
BOOTSTRAP_ADMIN |
최초 관리자 아이디 또는 이메일 |
BOOTSTRAP_ADMIN_PASSWORD |
최초 관리자 비밀번호(12자 이상) |
ENCRYPTION_KEY |
base64로 인코딩한 32-byte master key |
PDF 변환 동작만 조정하는 선택 환경변수가 두 개 있습니다.
| 환경변수 | 기본값 | 설명 |
|---|---|---|
MUNI_CHROMIUM_PATH |
자동 탐색 | PDF Export에 사용할 headless 브라우저 경로 |
MUNI_PDF_CONCURRENCY |
2 |
동시에 실행할 Chromium 프로세스 수(1~32) |
운영 정보
- 서비스 포트:
8080 - Liveness:
/healthz - Readiness:
/readyz - REST API:
/api/v1 - 공개 링크:
/s/{token}— 인증 없이 열리는 유일한 화면입니다 - OpenAPI:
/api/openapi.yaml— 실제 라우트와 일치하며, 테스트가 그것을 지킵니다 - Prometheus:
/metrics— 관리자 인증 필요 - MCP:
/mcp - 지원 DB: PostgreSQL 15 이상
- 이미지에는 PDF Export용 Chromium과 Noto CJK 글꼴이 포함되어 있습니다.
릴리스 파일 검증
- 파일:
muni-v0.29.0.tar.gz - 크기: (릴리스 후 기록)
- SHA-256: (릴리스 후 기록)
- 내부 이미지 태그:
muni:v0.29.0
sha256sum muni-v0.29.0.tar.gzGitHub Actions가 이미지 빌드, archive 생성, 내부 이미지 태그 검증을 완료한 뒤 이 asset을 게시했습니다.