우벨 · 어휘 스킨 설계 — 행정 편

행정 앞에서 멈춘 사전, 세 층으로 가른다

사전이 멈춘 것은 사전이 모자라서가 아니라, 사전에 들어갈 수 없는 것들이 사전 문 앞에 줄을 섰기 때문이다. 낱말·목록·방향은 서로 다른 재료이고, 우벨에는 이미 세 재료의 집이 각각 있다.

낱말은 사전에, 목록은 등록부에, 방향은 약속에. orgVocab.ts 는 계속 낱말 열몇 개짜리 얇은 파일로 남는다. 회의 종류·증명서 종류는 이미 살아 있는 org_kind_sections 곁의 DB 등록부로 가고, 청원의 올리는 쪽/심의하는 쪽은 조직 그래프의 약속에서 읽는다 — "약속에 적힌 것만 흐른다"는 원칙 그대로.
apps/web/lib/orgVocab.ts · 코드 ① 사전 — 낱말 모든 유형이 같은 문장을 말하는데 빈칸이 정확히 하나인 자리. 광고/공지/공문, 성도/학생, 회의록/노회록. 15개 안팎에서 멈춘다. 답하는 질문 — "이걸 뭐라고 부르나?"
org_kinds · org_kind_sections · org_kind_presets(신설) · DB ② 등록부 — 목록·존재 빈칸에 들어갈 값이 여러 개이거나, 화면 자체가 유형에 따라 있고 없는 자리. 당회·제직회…, 증명서 종류, 섹션 구성. 행이 없으면 화면이 없다. 답하는 질문 — "무엇이 몇 개 있나? 이 화면이 있나?"
조직 그래프 · 약속 4종 · DB ③ 약속 — 방향·역할 문장 틀 자체가 뒤집히는 자리. 청원을 올리는 쪽/심의하는 쪽은 kind 가 아니라 이 조직에 어떤 약속이 걸려 있는가로 정해진다. 답하는 질문 — "누가 누구에게? 어느 쪽 얼굴인가?"
A다섯 질문, 다섯 답

경계는 문장이 긋는다

각 답의 첫 줄이 규칙이고, 나머지는 그 규칙이 실전(방금 고친 버그·경고문 사고)에서 어떻게 작동하는지다.

Q1 · 사전의 경계는 어디인가?

모든 유형이 같은 문장을 말하는데 빈칸이 정확히 하나일 때만, 그 빈칸이 사전에 들어간다.

문장 틀 자체가 달라지면(청원 올림 ↔ 심의) 그건 낱말이 아니라 역할 — 약속 층으로. 빈칸에 들어갈 값이 여러 개면(당회·제직회·공동의회…) 그건 낱말이 아니라 목록 — 등록부 층으로. 이 한 줄이면 사전은 화면이 늘어도 자라지 않는다. 새 화면은 새 문장을 가져올 뿐이고, 그 문장의 빈칸은 대개 이미 사전에 있는 낱말이다. 유형×화면 곱셈은 규칙 위반의 증상이지 사전의 숙명이 아니다.

"{회의 종류}를 열고 회의록을 남깁니다" 앞 빈칸은 값이 여러 개 → 등록부. 뒤 빈칸은 하나(회의록/노회록) → 사전. 같은 문장 안에서도 층이 갈린다.

Q2 · 회의·증명서처럼 "목록"인 낱말은?

목록은 사전이 아니라 등록부(DB)다 — org_kind_sections 가 이미 낸 길을 그대로 넓힌다.

신설 org_kind_presets(kind_key, domain, label, sort, enabled) 한 테이블에 domain = 'meeting_kind' | 'cert_type' | 'petition_type' 로 담는다. 배열을 사전에 넣지 않는 이유 셋. ① 조직마다 다르게 쓰고 싶어질 축이 맞다 — 같은 kind='school' 안에서도 초·중·고는 학부모회, 호남신학대는 교수회의다. 뒤에 org 단위 덮어쓰기 한 층만 얹으면 되는 구조는 등록부뿐이다. ② 켜고 끄기·순서 바꾸기가 필요한데 TS 상수는 배포 없이는 못 한다. ③ 결정타 — 청원 안건은 심의자(노회)가 정의하고 청원자(교회) 화면이 그걸 읽어야 한다. 코드 상수로는 아예 표현이 안 되는 소유 관계다.

Q3 · 청원처럼 방향이 뒤집히는 것은?

방향은 kind 로도 사전으로도 가르지 않는다 — 이 조직에 걸린 약속(그래프의 관계)에서 도출한다.

"약속에 적힌 것만 흐른다." 청원도 흐르는 것이므로 약속을 따른다. 나가는 심의 약속이 있으면 청원자 얼굴, 들어오는 심의 약속이 있으면 심의자 얼굴, 둘 다면 두 탭(협의회가 교회에게 받으며 교단에 올리는 경우), 둘 다 없으면 화면 자체가 없다(학교 — 방금 고친 버그가 구조적으로 재발 불가능해진다). audienceIsOrgs 는 겸직을 해고하고 본업(문장의 단위 낱말 고르기)으로 복귀시킨다. kind 로 방향을 추측하는 isUpperOrg 분기는 약속 조회로 대체될 과도기 장치로 격하한다.

Q4 · 유형 8종을 다 채울 것인가?

사전은 전 유형 완주, 등록부는 빈 채로 정직하게. 없는 개념은 중립어로 때우지 않고 행이 없어서 화면이 안 뜬다.

사전의 낱말 15개는 전부 보편 개념이다 — 알림·구성원·명부·회의록 없는 조직은 없으므로 관공서·기타도 중립어로 다 채운다(지금 GENERIC 그대로). 반대로 당회·안수는 보편이 아니고, 보편이 아니라는 사실 자체가 그것이 사전이 아니라 등록부에 있어야 한다는 증거다. 관공서에 청원 화면을 "빈 값"으로 두면 깨진 화면이 되고 "중립어"로 두면 거짓 화면이 된다. 감추는 게 답인데, 감추는 판단을 코드 분기로 하면 원칙이 무너진다 — 등록부에 행이 없으면 안 뜬다. _default 폴백(회의·재정·증명)이 이미 그 관행이다.

Q5 · 낱말이 이 조직 것인지 하위 조직 것인지, 사전이 스스로 말하게 할 수 있나?

있다 — 필드 이름이 소유자를 말한다. 사전의 모든 낱말은 "이 조직 자신의 것"이고, 하위 조직 내부를 가리키는 낱말은 unit* 접두 필드로 따로 산다.

unitMember · unitRoster · unitLeader 를 신설한다. 노회의 roster는 "소속 교회 명단", unitRoster는 "각 교회의 명부" — 사고가 났던 그 경고문이 쓸 낱말은 처음부터 후자였다. audienceIsOrgs=false 인 조직은 두 층이 겹치므로 unit* 가 자기 낱말과 같은 값이다(교회의 unitRoster = 명부). 화면 코드 금칙 하나를 함께 세운다 — vocab 낱말 앞에 "각·소속·하위"를 붙여 합성하는 순간이 버그다. 그 합성어가 필요하면 사전 필드로 승격하라. 층을 문장이 아니라 타입이 지게 하는 것이다.

B확장된 사전의 모양

11 → 15. 그리고 거기서 멈춘다

기존 11개는 전부 유지(기존 규칙 — 권한·수신자 판정 금지 — 도 유지). 더해지는 것은 4개뿐이고, 각각 "같은 문장, 빈칸 하나" 검사를 통과한 낱말이다. 행정이 요구하는 나머지는 전부 다른 층으로 보낸다.

필드구분무엇을 담나왜 사전에 있어야 하나
유지 — 기존 11개
kind유지유형 이름 — 교회·학교·노회모든 문장의 주어 자리. 빈칸 하나.
post · postVerb유지전원 발신물과 그 서술어 — 광고를 보냅니다 / 공문을 내려보냅니다같은 문장 틀, 빈칸 둘이지만 각각 하나짜리.
member · roster유지이 조직 자신의 구성원과 그 표 — 성도/명부, 소속 교회/소속 교회 명단Q5 규칙에 따라 의미가 "자기 층"으로 좁게 못박힌다.
followUp · followUpTarget유지못 본 이에게 하는 일 — 심방 / 개별 연락확인 현황 화면의 핵심 문장.
leader · care유지맡은 사람·돌봄 축 — 목사님/목양, 선생님/생활지도호칭과 축 이름은 낱말 하나.
audienceIsOrgs유지
·감직
수신자가 사람이 아니라 하위 조직인가본업 복귀 — 문장 단위 고르기 표시로만. 청원 방향 판정 겸직은 약속 층이 가져간다.
audienceUnit유지세는 단위 — 명·교회·조직 ("분" 금지 규칙 포함)숫자 옆 낱말 하나.
신설 — 4개
minutes신설회의의 기록물 — 회의록 · 노회록 · 총회록"{…}을 남깁니다" — 같은 문장, 빈칸 하나. 지금은 meetings 화면 설명문에 하드코딩돼 있다.
unitMember신설하위 조직 안의 사람 — 노회에서 "성도"Q5. 상위 조직 화면이 두 층을 한 문장에 같이 쓴다("교회가 수락하면 성도에게…").
unitRoster신설하위 조직 안의 명부 — 노회에서 "각 교회의 명부"사고 재발 방지 필드. 경고문이 정반대가 됐던 자리에 들어갈 바로 그 낱말.
unitLeader신설하위 조직을 맡은 사람 — 노회에서 "담임 목사님"개통·초대 문장이 이미 쓰고 있는 말인데 지금은 하드코딩("담임 목사님 번호를 적으면…").
일부러 빠지는 것 — 다른 층의 재료 (상세는 E)
meetingKinds[]
certTypes[]
petitionTypes[]
등록부당회·제직회… / 교인·재직·세례… / 청빙·임직…값이 여러 개 + 조직별 커스텀 축 + 청원 안건은 심의자 소유. 셋 다 사전 실격 사유.
화면 제목·부제·섹션등록부"회의 관리"·"인사·고시"·묶음 라벨org_kind_sections 가 이미 소유. 머리말은 `${kind} 행정` 합성으로 충분.
청원 방향·역할약속올리는 쪽 / 심의하는 쪽 / 양쪽 / 없음낱말이 아니라 관계의 성질. 그래프에서 도출.
unit* 규칙 — audienceIsOrgs=false 면 두 층이 겹치므로 unitMember=member · unitRoster=roster · unitLeader=leader. 교회·학교·일반 조직에서 unit* 는 그냥 자기 낱말이다. 화면은 층을 따지지 않고 항상 올바른 필드를 골라 쓰면 되고, 값이 갈리는 것은 상위 조직에서뿐이다.
C유형 × 낱말 대조표

네 유형이 실제로 말하는 한국어

위 블록은 사전(코드), 아래 블록은 등록부(DB 시드)와 약속(그래프) — 같은 표에 두되 층 표시를 단다. 빈 칸은 빈 채로 두고 이유를 적었다. 노회 열은 교단·협의회·종교가 이름만 갈아 끼우는 자리다.

낱말 교회
광주양림교회
학교
호남신학대학교
노회 (교단·협의회)
전남노회
일반 조직
other · gov_office
① 사전 층 — orgVocab.ts
kind교회학교노회 · 교단 · 협의회조직 · 관공서
post광고공지공문알림
member성도학생·학부모소속 교회구성원 · 직원
roster명부명부소속 교회 명단명부
leader목사님선생님노회장님 · 총회장님 · 회장님담당자
care목양생활지도돌봄케어
minutes 신설회의록회의록노회록 · 총회록 · 회의록회의록
unitMember 신설= 성도 (겹침)= 학생·학부모 (겹침)성도= 구성원 (겹침)
unitRoster 신설= 명부 (겹침)= 명부 (겹침)각 교회의 명부= 명부 (겹침)
unitLeader 신설= 목사님 (겹침)= 선생님 (겹침)담임 목사님= 담당자 (겹침)
audienceUnit교회 · 조직
② 등록부 층 — org_kind_presets 시드 (조직이 덮어쓸 수 있다)
회의 종류 당회 · 제직회 · 공동의회 · 임원회 교무회의 · 운영위원회 · 이사회
시드는 초·중·고 기준. 호남신학대는 org 덮어쓰기로 교수회의·처장회의 — 같은 kind 안에서 갈리는 첫 실물이자, 이 축이 등록부여야 하는 증거.
정기회 · 임시회 · 상비부 · 임원회 정기 모임 · 운영진 회의 · 총회
증명서 종류 교인 · 재직 · 세례 · 출석 증명서 재직 확인서(교직원) · 활동 확인서 · 수료증
⚠재학·성적·졸업 증명은 넣지 않는다 — NEIS·정부24가 발급하는 법정 증명. 우벨이 흉내 내면 가짜 서류 제조기가 된다. 비법정 확인서만.
소속 · 재직 · 안수 증명서 회원(소속) 확인서 · 활동 확인서
관공서 시드는 이것도 없음 — 관공서는 증명을 받는 곳이 아니라 떼 주는 곳이라 우벨 발급이 성립 안 함. 행 없음 = 화면 없음.
청원 안건 안건은 심의자의 것. 교회 화면은 소속 노회가 등록한 안건(청빙 승인·임직·이명…)을 읽는다 심의 약속이 없으므로 읽을 안건도 없다 담임목사 청빙 승인 · 부목사 청빙 · 장로 임직 승인 · 목사 이명 · 목사 사임 · 교회 설립·변경 없음
③ 약속 층 — 그래프에서 도출 (사전·등록부 어디에도 저장하지 않는다)
청원 역할 올리는 쪽
전남노회로 나가는 심의 약속
역할 없음 → 화면 없음. 이사회 결재는 청원이 아니라 회의(이사회)의 일 심의하는 쪽
교단에 올리는 약속이 있으면 양쪽 — 두 탭
역할 없음 → 화면 없음
D행정 3화면 × 4유형

같은 화면이 네 조직에서 무엇이 되는가

각 화면의 문장 틀은 하나다. 틀 위에 사전 낱말(금색 밑줄)과 등록부 목록(칩)이 얹히고, 청원만 약속이 얼굴을 고른다. 목업의 데이터는 파일럿 실물 — 지어낸 활기는 없다.

1 · 회의 — 문장 틀은 하나, 층은 둘

"{등록부: 회의 종류}를 열고 minutes를 남깁니다" — 화면 제목·설명이 통째로 이 틀에서 나온다. isChurch 분기와 CHURCH_KINDS/TREE_KINDS 상수는 사라진다.
광주양림교회 · 교회 교회 행정

회의 관리

당회·제직회·공동의회를 열고 회의록을 남깁니다.

당회제직회공동의회임원회기타
당회예정2026년 8월 정기 당회날짜 미정 · 본당 소회의실
회의 열기
호남신학대학교 · 학교 학교 행정

회의 관리

교수회의·처장회의를 열고 회의록을 남깁니다.

교수회의처장회의이사회기타
아직 기록된 회의가 없습니다. 첫 회의를 열어보세요.
회의 열기

칩이 학부모회가 아닌 교수회의인 것은 org 덮어쓰기 — kind 시드(교무회의·운영위원회·이사회) 위에 대학이 자기 목록을 얹었다.

전남노회 · 노회 노회 행정

노회 회의

정기회·임시회를 열고 노회록을 남깁니다.

정기회임시회상비부임원회
정기회완료제182회 정기노회📝 노회록 · 양림교회당
회의 열기

"회의록" 자리가 전부 minutes="노회록"으로 갈린다 — 목록 라벨·상세 화면까지.

일반 조직 · other 조직 행정

회의 관리

모임을 열고 회의록을 남깁니다.

정기 모임운영진 회의총회기타
아직 기록된 회의가 없습니다.
회의 열기

2 · 청원 — 약속이 얼굴을 고른다

나가는 심의 약속 → 청원자 얼굴 · 들어오는 심의 약속 → 심의자 얼굴 · 둘 다 → 두 탭 · 둘 다 없음 → 화면 없음 kind 를 보지 않으므로 "학교가 노회 템플릿에 떨어지는" 사고 계열이 구조적으로 소멸한다. 안건 칩은 심의자의 등록부를 청원자가 읽는다.
광주양림교회 · 나가는 약속 → 전남노회 교회 행정 · 상호행정

청원·인가

소속 노회에 청빙·임직·이명 등을 청원하고 결과를 받습니다.

담임목사 청빙 승인장로 임직 승인목사 이명
장로 임직 승인심의 대기전남노회장로 2인 임직 승인의 건
청원 제출

안건 칩은 전남노회가 등록한 목록을 읽어온 것.

전남노회 · 들어오는 약속 ← 소속 교회 20 노회 행정

인사·고시

소속 교회가 올린 청원을 심의해 가결·부결합니다.

장로 임직 승인심의 대기광주양림교회장로 2인 임직 승인의 건
가결부결

교단으로 올리는 약속이 생기면 이 화면에 "교단에 청원" 탭이 하나 더 붙는다 — 얼굴 둘, 화면 하나.

호남신학대학교 · 학교

이 화면은 없다

학교에는 심의 약속이 걸려 있지 않다 — 청원 역할이 없으므로 등록부에 행이 없고, 행이 없으므로 행정 목록에 아예 나타나지 않는다. 이사회 결재가 필요하면 그것은 회의(이사회)의 일이다.

일반 조직 · other · gov_office

이 화면은 없다

중립어로 때운 "청원" 화면은 거짓 화면이다. 훗날 어떤 조직이 실제로 상급 단체와 심의 약속을 맺으면, 그 약속이 생기는 순간 화면이 나타난다 — 코드 수정 없이.

3 · 증명서 — 종류는 등록부, 없는 곳은 정직하게

"등록부: 증명서 종류를 발급하고 인쇄합니다" — CHURCH_TYPES/TREE_TYPES 상수가 등록부 행으로 이사한다.
광주양림교회 · 교회 교회 행정

서무·증명

증명서를 발급하고 인쇄합니다. 발행번호가 자동으로 매겨집니다.

교인 증명서재직 증명서세례 증명서출석 증명서
발급하기
호남신학대학교 · 학교 학교 행정

확인서 발급

우벨이 떼 줄 수 있는 것만 뗀다 — 비법정 확인서.

재직 확인서 (교직원)활동 확인서수료증
재학·성적·졸업 증명서는 여기 없다. 법정 증명은 학교 학사 시스템·정부24의 소관 — 우벨이 같은 이름의 서류를 발급하는 순간 신뢰가 아니라 위조의 도구가 된다. 종류 목록이 등록부인 덕에, 이 판단이 코드가 아니라 시드 데이터 한 줄로 지켜진다.
전남노회 · 노회 노회 행정

공문·증명

안수·소속·재직 증명을 발급합니다.

소속 증명서재직 증명서안수 증명서
안수 증명서2026-07-30윤재권JN-2026-0003 · 교단 제출용
발급하기
일반 조직 · other 조직 행정

확인서 발급

회원(소속)·활동 확인서를 발급합니다.

회원 확인서활동 확인서
발급하기

관공서(gov_office)는 이 화면도 없다 — 증명을 떼 주는 기관이 우벨로 확인서를 만들 일이 없으므로 등록부에 행을 심지 않는다.

E사전에 넣으면 안 되는 것

사전을 얇게 지키는 여덟 가지 거절

각 항목의 초록 글씨가 그것이 대신 가야 할 층이다. 거절의 공통 근거는 하나 — Q1의 한 줄 규칙을 통과하지 못한다.

목록 — 회의 종류·증명서 종류·청원 안건 → 등록부 (org_kind_presets)

빈칸 하나가 아니라 여러 개다. 유형×항목으로 곱해 부풀고, "우리 교회는 운영위원회"라는 조직별 취향이 반드시 붙는 축이라 켜고 끄고 덮어쓸 수 있는 DB 행이어야 한다.

화면의 존재 여부 → 등록부의 행 유무 (org_kind_sections)

사전은 전 유형이 전 필드를 채우는 완주형이라 "없음"을 표현할 수 없고, 표현하려 드는 순간 빈 값·중립어 같은 거짓말이 생긴다. 부재는 행이 없다는 사실로 말한다.

화면 제목·부제·아이콘·묶음 라벨 → 등록부 (org_kind_sections — 이미 그렇게 산다)

"교회 행정/노회 행정" 하드코딩의 답은 사전 필드 추가가 아니라 `${vocab.kind} 행정` 합성이다. 섹션 제목은 000120부터 이미 DB가 소유한다.

방향·역할 — 올리는 쪽 / 심의하는 쪽 → 약속 (조직 그래프)

낱말이 아니라 관계의 성질이고, 같은 kind 라도 걸린 약속에 따라 다르다(협의회는 양쪽일 수 있다). 사전에 넣으면 audienceIsOrgs 겸직 사고를 하나 더 만드는 것이다.

권한·수신자 판정·계층 판단 → 서버 (기존 금지 유지)

orgVocab.ts 머리말이 이미 세운 금지. 실수신자는 org_announcement_recipients(000175)가, 접근은 RLS 가 정한다. 사전은 문장을 고를 뿐 문을 열지 않는다.

문장 전체 — 경고문·설명문·빈 상태 문구 → 화면 (컴포넌트 소유)

사전이 문장을 갖는 순간 화면 수만큼 자란다. 화면이 문장 틀을 소유하고 사전은 빈칸만 메운다. 단, 화면이 낱말 앞에 "각·소속·하위"를 붙여 합성하면 그것이 Q5 사고다 — 합성어가 필요하면 unit* 필드로 승격.

법정 서식·증명서 본문 양식·직인 → 문서 템플릿 층 (발급 화면과 별개)

증명서의 본문 문구·서식은 낱말이 아니라 문서다. 그리고 재학·성적처럼 법이 발급 주체를 정한 서류는 어느 층에도 넣지 않는다 — 만들지 않는 것이 설계다.

개별 조직의 낱말 취향 → org 단위 덮어쓰기 (등록부의 다음 층)

사전은 유형의 것이다. "우리는 교수회의라고 부른다"는 kind='school' 의 사전을 고칠 일이 아니라 호남신학대 한 조직의 등록부 행을 덮어쓸 일이다. 취향이 사전에 스미면 유형이 조직 수만큼 쪼개진다.