색만 달랐던 알약 배지는 흑백 출력과 색약 상황에서 "운영 중"과 "개방불가"가
똑같이 보였다. 모양이 상태를 함께 담도록 바꾼다.
- ui/StatusGlyph: 90도와 45도만 쓴 10px 글리프 8종
- ui/StatusChip: 오른쪽 위 모서리를 잘라낸 태그 + 왼쪽 2px 컬러 바.
표에서 바가 세로로 정렬돼 목록을 훑기 쉬워진다
- codes/tone: tone에서 칩 클래스와 기본 글리프를 파생
적용: 채널 준비 상태, 권리확인/권리처리 판정, 담당자 구분, 단계 태그,
타임라인(이모지 제거), 업무단계 스트립 완료 표시(글꼴 체크 제거 - OS마다
굵기와 곡률이 달랐다)
필터 칩과 아바타는 둥근 모양 그대로 뒀다. 둥글면 누를 수 있는 것,
각지면 사실을 알려주는 것으로 구분된다.
Co-Authored-By: Claude Opus 5 (1M context)
공통코드 도입과 260727 검토본 요구사항 반영을 한 번에 올린다. 커밋 16개.
주요 변경
· 선택지를 코드테이블로 옮기고 소스에서 코드값 리터럴 제거
(처리완료 13곳·공공누리 3곳·담당자 5곳·진행단계 5곳 → 각 1곳)
· 진행단계 13단계 신설, 완료 기준을 보고서 제출 완료로 이동
· 자료 업로드에 따른 자동 단계 전환 3종과 이력 자동/수동 구분
· RE 메모, 자료실, 보고서 수정, 일괄등록 비고 신설
· 작성자 표기를 로그인 아이디에서 표시 이름으로
함께 고친 결함
· 권리확인 기존 공공누리 유형이 비어 보이던 문제 (1,926건)
· 권리처리 엑셀 왕복 시 기존 증빙자료가 사라지던 문제
· 공공누리 유형 표기를 N유형으로 통일 (68건)
배포 시 주의
마이그레이션 V13~V18이 함께 적용된다. 운영 DB에 코드표에 없는 값이 남아
있으면 해당 자료의 선택 칸이 비어 보이므로, 배포 전
src/main/resources/db/consistency/check-orphan-codes.sql 을 돌려
발견된 값을 비활성 코드로 등록해야 한다.
테스트: 백엔드 288건, 프런트 209건 전부 통과.
[18] 목록접수 조건이 자료에 적힌 것과 달랐다.
자료 : 「목록접수 = Mattermost에 첫 자료 업로드 + 핀 고정 시」
구현 : 파일이 올라가기만 하면 넘어감 (핀 고정 조건 누락)
공지 고정이 끝난 뒤에 판단하도록 옮겼다. 자료만 올리고 고정하지 않은 경우, 자료 없이
공지만 올린 경우, 고정에 실패한 경우 모두 넘어가지 않는다. 다섯 갈래를 테스트로 잠갔다.
[통일] 공공누리 유형 표기를 「N유형」으로 맞춘다.
조사표에 「제4유형」으로 적혀 온 자료가 68건 있었는데 나머지는 모두 「0~4유형」
형태였다. 같은 유형인데 유형별 집계에서 따로 잡히던 문제다.
세 곳을 함께 손봤다.
· 기존 데이터를 정규식으로 일괄 변환(V18). 뜻이 다른 「유형 확인 필요」는 건드리지 않는다
· 「제4유형」을 선택지에서 제외
· 업로드 시점에도 옛 표기를 표준 표기로 바꿔 넣는다 - 이게 없으면 기관이 예전 파일을
다시 올릴 때 되살아난다
「제4유형」 코드 행은 지우지 않고 비활성 별칭으로 남긴다. 업로드 변환이 이 행의 sameAs를
보고 동작하기 때문이다. 코드표가 별칭까지 관리하는 셈이라 앞으로 다른 표기가 발견돼도
데이터 한 줄로 흡수할 수 있다.
백엔드 288건 통과.
Co-Authored-By: Claude Opus 5 (1M context)
검토본의 답변은 빨간 글씨 + 밑줄 체크로도 표시돼 있었는데, 처음 읽을 때 글자 색을
빼고 텍스트만 뽑아 10개 항목을 "무응답"으로 잘못 분류했다. 색까지 다시 읽어 확인한
결과 12번(이해 안 감)을 뺀 27개 항목에 모두 답이 있었다.
그중 손대야 하는 4개를 반영한다. 나머지 6개(1·3·9·13·14·24)는 "현행 유지"라
이미 맞는 상태다.
[18] 자동 단계 전환 3종을 되살렸다.
채널에 자료 업로드 → ②목록접수
권리확인 자료 업로드 → ⑤법률검토(권리확인)
권리처리 자료 업로드 → ⑧법률검토(권리처리)
앞으로만 간다. 이미 그보다 앞서 있으면 아무 일도 하지 않는다 - 자료를 다시
올렸다고 단계가 뒤로 밀리면 안 된다. 채널이 없어 단계가 아직 없는 기관도
건드리지 않는다.
[19] 되돌림을 허용하고, 이력에 자동/수동 구분과 계기를 남긴다.
[22] 업무메모·연락이력·보고서 작성자가 로그인 아이디(admin)가 아니라 표시 이름으로
찍힌다. 담당자별 계정이 생기면 CurrentUser 한 곳만 고치면 된다.
[26] 자료실을 열었다. 기관에 딸리지 않는 공용 서식을 올려두고 내려받는다.
어느 자료가 어느 단계에 해당하는지는 자료의 지시를 이 시스템 흐름에 맞춰 옮긴 것이라
답변 문서에 적어 확인 요청해 둔다.
백엔드 281건, 프론트 209건 통과.
Co-Authored-By: Claude Opus 5 (1M context)
[4][5] RE 메모 기관관리 RE 탭을 메모장처럼 만들었다. 메모 1개가 RE 1개다.
내용·작성자·일자에 완료 체크 한 칸을 더했고, 대시보드 '미해결 RE'가
이제 실제 숫자를 센다(예전에는 항상 0으로 고정돼 있었다).
완료 체크는 회신에 없던 항목이라 확인 요청해 둔 상태다.
[6] 업무구간 필터를 뺐다. 현재 단계와 겹친다는 지적을 반영했고 순서도 현재 단계를
앞으로 옮겼다. 구간 구분 자체는 칸반 컬럼 색에 계속 쓰이므로 남겨 둔다.
[8] 자료접수를 권리확인·권리처리 통합으로 바꿨다. 어느 쪽이든 한 건이라도 있으면 접수다.
[15] 일괄 등록에 비고를 더했다. 권리확인·권리처리 양쪽 모두.
[25] 보고서 교체 기능을 넣었다. 목록에 새 줄이 생기지 않고 그 자리만 바뀐다.
개정판으로 남기고 싶으면 지금처럼 새로 올리면 된다.
RE 메모의 해결 여부를 뒤집으면 대시보드 숫자가 실제로 따라 움직이는지까지 테스트한다.
백엔드 275건, 프론트 208건 통과.
Co-Authored-By: Claude Opus 5 (1M context)
설계서에서 "업로드와 다운로드가 따로 정의돼 어긋날 수 있다"고 적었던 그 결함이
실제로 나 있었다.
다운로드 기존 증빙자료를 헤더 자리인 T열(19)에 쓴다
업로드 U열(20)을 읽는다
기관 원본 파일은 헤더가 T인데 데이터가 U에 있어서(T에는 엑셀 화면용 '수정' 링크가
들어 있다) 파서가 U를 읽도록 돼 있었는데, 이 시스템이 만들어 주는 다운로드 파일은
T에 쓴다. 그래서 내려받아 고쳐서 다시 올리면 그 칸만 조용히 비워졌다.
파서가 U를 먼저 보고 비어 있으면 T를 보게 했다. 한쪽만 맞추면 다른 쪽이 깨지므로
둘 다 읽는다.
왕복 테스트를 먼저 만들어 결함을 재현한 뒤 고쳤다. 헤더 이름과 파서 컬럼이 같은
자리를 가리키는지도 24개 필드 전부 확인한다 - 앞으로 한쪽만 고치면 여기서 깨진다.
덤: PowerShell로 파일을 손보다 들어간 BOM 14건을 제거했다.
백엔드 270건, 프론트 208건 통과.
Co-Authored-By: Claude Opus 5 (1M context)
SQL 5곳(ProcessItemMapper 4 + DashboardMapper 1)과 자바 화이트리스트 1곳에 '처리완료'가
남아 있었다. 쿼리는 코드표를 참조할 방법이 없어 서비스가 값을 읽어 파라미터로 넘긴다.
처리상태 검증도 코드표를 따른다. 상태를 하나 더 만들어도 자바를 안 고쳐도 된다.
지정이 없을 때는 빈 문자열이 아니라 매칭되지 않는 값을 넘긴다. 빈 문자열을 주면
처리상태가 비어 있는 행이 전부 완료로 잡힌다.
파라미터화가 형식만 바뀐 게 아님을 확인하는 테스트를 넣었다. 코드표에서 done 속성을
'미처리' 쪽으로 옮기면 진행률과 상태 필터가 실제로 뒤집힌다 - 예전 구조에서는 코드표를
바꿔도 아무 일도 일어나지 않았다.
이로써 조사에서 세었던 13곳이 모두 정리됐다.
백엔드 266건 통과.
Co-Authored-By: Claude Opus 5 (1M context)
처리상태 미처리/처리완료를 코드표로. 어느 값이 완료인지는 done 속성이 정한다
필터 칩 서버에 넘기는 키(PENDING/DONE)도 done 속성에서 나온다
계약서류 6종 '기타'는 freeText 속성으로 구분해 자유 입력을 붙인다
공공누리 유형 권리확인과 같은 코드표를 쓰되 process scope라 '보류'가 안 나온다
프론트에 흩어져 있던 '처리완료' 비교 6곳이 사라졌다. 남은 것은 SQL 5곳과
자바 1곳이고 Phase 3에서 정리한다.
이로써 화면 8개가 모두 코드표를 본다. 각 화면 상단의 옵션 배열은 전부 없어졌다.
백엔드 263건, 프론트 208건 전부 통과.
Co-Authored-By: Claude Opus 5 (1M context)
이 파일 상단에 옵션 배열과 판정 규칙이 90줄 가까이 박혀 있었다. 전부 코드표로 옮겼다.
대분류/세부 추천값 이름을 직접 비교하던 if 사슬을 suggest 속성 조회로 바꿈
세부별 허용 처리결과 ALLOWED_RESULTS_BY_MINOR 맵을 allowed 속성으로
세부 표시명·부연설명 REVIEW_MINOR_DISPLAY를 label·note로
처리결과 뱃지색 Tailwind 클래스 맵을 tone 토큰으로
공공누리 유형 3중 정의(7개/5개/6개)를 scope 하나로 통합
판정 매트릭스는 종이 양식과 같은 자리에 칸을 놓아야 해서 목록을 훑지 않고
majors[0], minors[1] 처럼 자리로 집는다. 배치는 그대로고 값의 출처만 바뀌었다.
값이 바뀔 때 폼을 채우는 동작(기존 유형 복사·비고 문구 덧붙임)은 코드에 남겼다.
데이터로 옮기면 코드표 안에 규칙 언어를 만드는 꼴이 된다. 문구는 messages.ts로 뺐다.
덤으로 실제 버그가 하나 고쳐진다. 기존 공공누리 유형 드롭다운이 0~4유형만 갖고
있어서, 운영 DB에 '유형 확인 필요'(1,858건)·'제4유형'(68건)으로 저장된 1,926건이
상세 화면에서 선택 안 됨으로 보이고 있었다. 이제 prior scope에 그 값들이 들어간다.
프론트 208건 전부 통과.
Co-Authored-By: Claude Opus 5 (1M context)
대시보드는 단계를 안 바꾸고는 코드표에 연결할 수 없는 구조였다. 칸반 컬럼 수가 단계
배열 길이에서 나오고 완료 판정이 11로 박혀 있었는데 코드표는 13을 말하기 때문이다.
프론트만 바꾸면 화면과 서버 숫자가 어긋나므로 백엔드까지 한 번에 옮긴다.
[11] 칸반 컬럼이 단계 미지정 + 13개가 된다. 단계 상한도 코드표 개수를 따른다.
[2] 완료 = ⑬보고서 제출 완료. 보고서가 실제로 올라온 기관만 센다.
[7] 진행상태에 '신청'이 생긴다. 신청은 ①만, 완료는 ⑬만, 나머지가 진행중이다.
채널이 없어 단계가 아예 없는 기관은 '단계 미지정'으로 그대로 둔다.
원문자 배열(①~⑫)과 구간 색 맵을 지웠다. 원문자는 번호에서 계산하고 색은 코드표의
tone에서 온다 - 단계가 늘 때마다 같이 늘려야 했던 자리다.
stages.ts를 지웠다. 남아 있던 소비자(기관상세·타임라인·시스템관리)도 모두 옮겼다.
동작이 바뀐 테스트 3건은 새 기준으로 다시 썼다(11단계 완료 집계, 13단계 거부,
마지막 단계 12). 도움말 문구도 13단계 기준으로 고쳤다.
백엔드 263건, 프론트 208건 전부 통과.
Co-Authored-By: Claude Opus 5 (1M context)
담당자관리는 구분 5종이 한 파일 안에서만 네 번 따로 적혀 있었다(표시명·뱃지색·필터칩·
드롭다운). 전부 코드표에서 가져오게 바꿨다.
사업관리의 단계 필터는 Array.from({length: 12})로 12가 박혀 있어서, 13단계를 늘려도
이 필터만 12개로 남을 자리였다. 연락 방법 4종도 코드표로 옮겼다.
동작은 그대로다. 프론트 208건 전부 통과.
Co-Authored-By: Claude Opus 5 (1M context)
로그인 직후 /api/meta/codes를 한 번 불러 앱 전체가 나눠 쓴다. 화면마다 따로 부르면
선택지를 기다리느라 폼이 깜빡인다.
선택지를 못 받으면 화면을 아예 안 그리고 오류를 보여준다. 빈 드롭다운을 그려두면
거기서 저장할 때 값이 지워지기 때문이다.
색은 DB에 tone(의미 토큰)만 두고 Tailwind 클래스는 프론트가 붙인다. 조립식 클래스명은
빌드 시점에 지워지므로 맵을 풀어 썼다.
useStages()가 단계 수·라벨·원문자·구간·진행상태·KPI 임계값을 코드표에서 가져온다.
원문자는 저장하지 않고 번호에서 계산한다.
테스트는 CodeProvider에 codes를 직접 넘겨 서버 없이 돈다. 픽스처는 시드와 같은 값이라
화면 테스트가 실제 선택지로 검증된다.
아직 각 화면은 기존 하드코딩 배열을 쓴다. 화면 전환은 다음 커밋부터.
Co-Authored-By: Claude Opus 5 (1M context)
시드를 쓸 때 화면 소스를 안 보고 의미만으로 색을 골랐던 탓에 두 가지가 어긋나 있었다.
Phase 2에서 화면이 코드테이블을 보게 되면 조용히 바뀔 값들이다.
업무구간 색 assign violet->indigo, check sky->emerald, closing emerald->violet
담당자 구분 MJ 표시명 주관기관->문정원 (주관기관은 엑셀 표기라 importAlias로)
뱃지색 5개 전부 화면 기준으로 교정
이미 적용된 V13을 고치면 체크섬이 어긋나 기동이 실패하므로 V15로 앞으로 나아가며 고친다.
어긋남이 다시 생기지 않도록 화면 기준값을 테스트로 잠갔다.
STAGE_GROUP의 cardBody 속성은 뺐다. 칸반 카드 본문은 구간이 아니라 단계 번호로 갈리는
자리가 있어(1단계는 신청일, 2·3단계는 목록 건수) 화면 로직으로 두는 편이 정확하다.
Co-Authored-By: Claude Opus 5 (1M context)
배포 전 정합성 점검을 실제 DB에 돌려보니 코드테이블에 없는 값이 1,926건 있었다.
review_item.kogl_type = 유형 확인 필요 1,858건
review_item.kogl_type = 제4유형 68건
둘 다 기관 조사표에서 온 값이라 prior(기존 부착 유형) 자리에만 넣고, 변호사가 고르는
판정값(review/process)에는 넣지 않는다. surveyOnly 속성으로 구분하고 그 구분이 새는지를
테스트로 막는다.
등록하지 않았다면 Phase 2에서 이 1,926건이 상세 화면에 선택 안 됨으로 보였을 것이다.
Co-Authored-By: Claude Opus 5 (1M context)
운영 DB에는 정책 변경 전 값이나 공백만 다른 값이 남아 있을 수 있다. Phase 2에서
화면이 코드테이블만 보게 되면 그런 값은 선택지에서 조용히 사라지므로, 배포 전에
찾아내 active=false 코드로 보존해야 한다.
Phase 1 회귀 확인: 백엔드 255건(기존 221 + 신규 34), 프론트 191건 전부 통과.
기존 동작은 바뀌지 않았다.
Co-Authored-By: Claude Opus 5 (1M context)
CodeService가 기동 후 첫 조회에 코드를 한 번 읽어 메모리에 들고 있는다. 지연 로딩인
이유는 Flyway 마이그레이션보다 빈 초기화가 먼저 돌 수 있기 때문이다.
활성 목록과 전체 목록을 나눠 들고 있는다. 화면 선택지에는 활성만 나가지만, 이미
저장된 과거 값의 라벨을 그리려면 비활성도 조회돼야 한다.
GET /api/meta/codes는 아직 소비자가 없다. 화면 전환은 Phase 2다.
Co-Authored-By: Claude Opus 5 (1M context)
진행단계는 요구사항 [7][11]대로 13단계로 넣고, 진행상태(신청/진행중/완료)와
KPI 임계값을 attrs로 데이터화했다. 요구사항 [2](보고서가 올라와야 최종완료)는
completed 임계값을 13번에 두는 것으로 해결된다.
공공누리 유형은 scopes로 사용처(review/process/prior)를 구분한다. 세 화면의
목록이 7/5/6개로 달랐던 것이 실수가 아니라 자리 차이였기 때문이고, 이 구분 없이
합치면 권리처리 화면에 보류가 잘못 노출된다.
Co-Authored-By: Claude Opus 5 (1M context)
코드값을 소스가 아니라 데이터로 관리하기 위한 기반. 저장값은 지금 업무 테이블에
들어 있는 한글 문자열을 그대로 쓴다 - 영문 키로 바꾸면 기존 데이터 변환과 엑셀
양식 수정이 딸려와 위험 대비 실익이 없다.
설계서와 Phase 1 구현계획도 함께 넣는다.
Co-Authored-By: Claude Opus 5 (1M context)
서버에서 itnhub.env를 Windows에서 만들어 올리면 source 할 때 줄 끝 캐리지 리턴이
값에 그대로 붙는다. DB 비밀번호나 SERVER_PORT가 조용히 틀어지고 빈 줄에서는
command not found 가 뜬다. 캐리지 리턴을 걷어낸 사본을 만들어 읽도록 바꾼다.
원본 파일은 그대로 둔다.
alpine에서 bash -n, shellcheck, CRLF env 주입까지 확인했다.
Co-Authored-By: Claude Opus 5 (1M context)
Windows에서 작업하고 리눅스 서버에서 실행하는 파일인데, git이 체크아웃할 때 CRLF로
바꿔놓아 bash가 syntax error로 죽었다(shellcheck SC1017). .gitattributes로 *.sh 와
Dockerfile, compose 파일의 줄바꿈을 LF로 고정하고 기존 파일을 정규화한다.
ps1은 Windows 전용이라 CRLF로 둔다.
start.sh는 포트를 파일 위쪽 설정 블록의 APP_PORT=8877 로 끌어올려, 기동과 확인이
같은 값을 쓰도록 정리했다. env 파일에 SERVER_PORT가 있으면 그 값이 우선한다.
alpine 컨테이너에서 bash -n 과 shellcheck로 확인했다.
Co-Authored-By: Claude Opus 5 (1M context)
application.yml에 server.port가 없어 기본 8080으로 뜨고, 실행 스크립트마다 --server.port를
따로 넘기고 있어서 환경별로 포트가 달랐다. 앱 자체 기본값을 8877로 두어 어디서 띄우든
같은 포트가 되게 한다. SERVER_PORT 환경변수로는 여전히 덮어쓸 수 있다.
함께 맞춘 곳:
- Dockerfile EXPOSE / HEALTHCHECK, docker-compose 포트 매핑
- deploy/start.sh 기본값, itnhub.env.example (SERVER_PORT·TZ 항목 추가)
- vite 개발 프록시 대상(:8080 -> :8877). 이걸 안 바꾸면 npm run dev 에서 /api 가 죽는다
- run-local.ps1 은 --server.port 인자를 떼고 앱 기본값을 따르게 함
- README / deploy/README 의 포트 표기
로컬에서 재빌드 후 확인: 8877 /login 200, 8080 닫힘.
Co-Authored-By: Claude Opus 5 (1M context)
기능이 대폭 늘었는데 README는 기관 명부·채널 생성까지만 설명하고 있어 실제와 어긋났다.
대시보드·사업관리·시스템관리·권리확인/처리·도움말까지 반영하고, 읽는 순서대로 다시 배치한다.
- 화면 구성, 12단계 흐름, 최초 사용 순서를 표와 도식으로 정리
- 환경변수를 필수 8개와 기본값 있는 선택 2개로 나눠 표기
- 수정할 때 깨뜨리면 안 되는 약속(채널 2종, 재실행 안전, 채널⇔단계 불변식,
엑셀 재업로드 병합 규칙, 제3자 권리처리 정책, 연번 문자열)을 한 표로 모음
- 패키지·화면 구조와 API 목록, 배포(deploy/README.md 연결) 추가
Yona 렌더러에서 깨지지 않도록 GitHub 전용 문법(경고 블록, HTML 표) 대신
표준 마크다운만 쓴다.
Co-Authored-By: Claude Opus 5 (1M context)
멀티스테이지로 프런트(React)까지 이미지 안에서 빌드해 단일 jar로 만든다.
빌드 머신에만 인터넷이 필요하고(Node v20.17.0·npm·Maven 내려받음) 실행 서버에는
JDK도 Node도 깔 필요가 없다.
- 컨테이너 시간대를 Asia/Seoul로 고정한다. UTC로 두면 업무단계 도달 일시,
칸반 카드의 D+n, 연락 이력 날짜가 9시간 어긋난다.
- 비루트 사용자로 실행하고, actuator가 없으므로 헬스체크는 /login 200으로 한다.
- 이미지 빌드 중에는 테스트를 돌리지 않는다(-DskipTests). Testcontainers가 Docker를
다시 요구해 Docker-in-Docker가 되기 때문이며, 테스트는 CI에서 별도 단계로 돌린다.
- .dockerignore로 run-local.ps1을 제외한다(DB 비밀번호가 들어 있다).
deploy/는 192.168.0.176 운영 정의다. DB는 기존 192.168.0.60을 그대로 쓰므로 컨테이너로
띄우지 않고, 앱이 로컬 디스크에 쓰는 것이 없어(업로드는 DB와 Mattermost로 간다)
영속 볼륨도 두지 않는다. 실제 값은 서버의 /etc/itnhub/itnhub.env 에만 두고
저장소에는 .example만 남긴다.
로컬에서 이미지 빌드·기동까지 확인했다: jar 안에 static/index.html 포함, 컨테이너
시간대 KST, /login 200, HEALTHCHECK healthy, Flyway "schema itnhub: 12, up to date".
Co-Authored-By: Claude Opus 5 (1M context)
요청자 피드백 PPT 2건(기능확인 24쪽, 문정원 검토내용 17쪽)에서 확인된
요구를 다음과 같이 반영한다.
- 대시보드: KPI 7타일 + "단계 미지정" 포함 13컬럼 칸반(카드 드래그로 단계 이동)
+ 최근 진행 기관. 집계는 /api/dashboard 한 번으로 끝낸다 - 기관별로 건수를
반복 조회하면 화면 한 번에 150회 조회가 되기 때문이다.
- 사업관리: 3단계 대상 기관 현황, 신청 확인 연락 이력, 기관별 최종 보고서.
기관 현황 숫자는 대시보드와 같은 집계를 재사용해 두 화면이 어긋나지 않게 한다.
- 시스템관리: 채널 초기화(대상 미리보기 → 확인문구 입력 → 서버 재검사).
삭제가 아니라 보관(아카이브)이며 DB가 아는 채널만 대상으로 한다.
- 권리확인: 제3자 권리처리 정책변경 반영. "권리처리 추진 미희망"과 공공누리
"보류"를 신설하고, 처리결과에 따라 공공누리 유형·비고를 규칙대로 자동 채운다.
- 권리확인/권리처리: 일괄 등록·선택 삭제와 열별 조건 복합검색(AND).
- 업무메모: 작성자를 화면에서 고르지 않고 로그인 사용자를 서버가 기록하며,
수정 기능을 추가한다(작성자·작성시각은 유지).
- 기관관리: 채널에 고정된 최초 안내글을 확인하는 [최초 게시글 보기].
- 도움말: 주요 버튼 옆 (?) 모달 15개 항목. 실제 화면 캡처를 함께 담는다.
마이그레이션 두 건:
- V11 "채널 있음 <=> 단계 있음" 불변식을 맞추는 1회성 백필. 단계 기능(V8)
이전에 채널을 만든 기관이 칸반에서 사라지는 것을 막는다.
- V12 사업관리용 contact_log / org_report.
테스트: 백엔드 221건, 프런트 191건 전부 통과.
Co-Authored-By: Claude Opus 5 (1M context)