Labor 테마 — Notion 설정값 가이드
themes/labor 테마에서 Notion 설정 테이블(Config Table) 로 바꿀 수 있는 값들을 설정명 ↔ 값 예시 쌍으로 정리했습니다.
설정 방법
Notion 설정 페이지의 테이블에 행을 추가합니다.
| 사용(Enable) | 설정명(Name) | 설정값(Value) |
|---|---|---|
✅ Yes | LABOR_HERO_LEFT_BANNER_IMAGE | https://example.com/left.jpg |
- 사용 칸은 정확히
Yes여야 적용됩니다. - 우선순위: Notion 설정값 > 테마
config.js기본값. (Notion에 없으면config.js값이 적용) - 값 타입
- 문자열: 그대로 입력 (
tangly1024,https://...) - 불리언:
true/false - 배열·객체: JSON 문자열(큰따옴표). 예:
[{"title":"공지","url":"https://..."}]
- 문자열: 그대로 입력 (
type=Config페이지가 여러 개면 첫 번째 1개만 읽으므로 한 페이지에 모으세요.
1. 레이아웃 · 일반
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_HOME_POST_TWO_COLS | true | 홈 블로그 글 목록 2열 표시 (false면 1열) |
LABOR_LOADING_COVER | true | 페이지 로딩 시 마스크 애니메이션 |
LABOR_SITE_CREATE_TIME | 2026-06-17 | 사이트 개설일(운영 일수 계산) |
LABOR_HERO_REVERSE | false | 히어로 좌·우 컴포넌트 위치 반전 |
LABOR_HERO_BODY_REVERSE | false | 본문 영역 좌·우 컴포넌트 위치 반전 |
2. 공지 바 (상단 스크롤)
| 설정명 | 값 예시 (JSON) | 설명 |
|---|---|---|
LABOR_NOTICE_BAR | [{"title":"환영합니다","url":"https://blog.example.com"},{"title":"문서 보기","url":"https://docs.example.com"}] | 비우려면 [] |
3. 히어로 좌측 광고 배너
이미지가 비면 그라데이션 배경. URL이 외부(http)면 새 탭으로 열림.
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_HERO_LEFT_BANNER_IMAGE | /images/labor/left.webp | 배경 이미지(URL/경로). 비우면 그라데이션 |
LABOR_HERO_LEFT_BANNER_TITLE_1 | 블로그 | 소형 타이틀(상단) |
LABOR_HERO_LEFT_BANNER_TITLE_2 | 프로그래밍 공유 그리고 사고와 인지 | 대형 타이틀 |
LABOR_HERO_LEFT_BANNER_URL | / | 클릭 시 이동 링크 |
4. 히어로 우측 광고 배너
좌측과 동일 구조, 독립 설정.
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_HERO_RIGHT_BANNER_IMAGE | /images/labor/right.webp | 배경 이미지. 비우면 그라데이션 |
LABOR_HERO_RIGHT_BANNER_TITLE_1 | 새 버전 출시 | 소형 타이틀 |
LABOR_HERO_RIGHT_BANNER_TITLE_2 | NotionNext 4.0 손쉬운 커스터마이징 | 대형 타이틀 |
LABOR_HERO_RIGHT_BANNER_URL | https://example.com | 클릭 시 이동(외부면 새 탭) |
5. 히어로 상단 고정 카테고리 (2개)
값은 {title, url} 객체의 JSON 문자열.
| 설정명 | 값 예시 (JSON) | 설명 |
|---|---|---|
LABOR_HERO_CATEGORY_1 | {"title":"필독 추천","url":"/tag/필독"} | 첫 번째 카테고리 버튼 |
LABOR_HERO_CATEGORY_2 | {"title":"인기 글","url":"/tag/인기"} | 두 번째 |
6. 우측 프로필 카드
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_INFOCARD_GREETINGS | ["안녕하세요! 저는","🔍 공유와 도움","🤝 디자인 전공"] | 인사말 배열(클릭 시 자동 전환) |
LABOR_INFO_CARD_URL1 | https://example.com/about | 버튼1 링크 |
LABOR_INFO_CARD_ICON1 | fas fa-user | 버튼1 아이콘(FontAwesome) |
LABOR_INFO_CARD_URL2 | https://github.com/your-id | 버튼2 링크 |
LABOR_INFO_CARD_ICON2 | fab fa-github | 버튼2 아이콘 |
LABOR_INFO_CARD_URL3 | https://example.com | 버튼3(더 알아보기) 링크 |
LABOR_INFO_CARD_TEXT3 | 더 알아보기 | 버튼3 텍스트 |
7. 소셜 · 커뮤니티 카드
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_INFO_CARD | true | 우측 정보 카드 표시 여부 |
LABOR_SOCIAL_CARD | true | '커뮤니티 참여' 카드 표시 여부 |
LABOR_SOCIAL_CARD_TITLE_1 | 인사관리 | 소형 타이틀 |
LABOR_SOCIAL_CARD_TITLE_2 | 스마트 인사관리 | 대형 타이틀 |
LABOR_SOCIAL_CARD_TITLE_3 | 커뮤니티 참여하기 | 버튼 텍스트 |
LABOR_SOCIAL_CARD_URL | https://www.facebook.com/your-page | 버튼 링크 |
8. 하단 통계 패널 문구
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_POST_COUNT_TITLE | 글 수: | 글 수 라벨 |
LABOR_SITE_TIME_TITLE | 사이트 개설 일수: | 운영 일수 라벨 |
9. 메뉴 표시
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_MENU_INDEX | true | 홈 메뉴 |
LABOR_MENU_CATEGORY | true | 카테고리 메뉴 |
LABOR_MENU_TAG | true | 태그 메뉴 |
LABOR_MENU_ARCHIVE | true | 아카이브 메뉴 |
LABOR_MENU_SEARCH | true | 검색 메뉴 |
9-1. 상단 그룹 메뉴 동작 (labor 전용)
labor는 공용 문서(메뉴 Menu / SubMenu)에 설명된 2단계 드롭다운을 쓰지 않는다. Notion 메뉴 구성은 그대로 쓰되 화면 동작이 다르다.
| Notion 설정 | labor에서의 동작 |
|---|---|
Menu (하위 SubMenu 있음) | 드롭다운을 열지 않고 /group/{메뉴명}으로 이동. 그 그룹에 속한 글을 모두 보여 준다 |
Menu (하위 없음) | 설정한 링크로 그대로 이동 |
SubMenu | 화면에 목록으로 펼치지 않는다. 대신 그 그룹에 어떤 분류가 속하는지를 정하는 근거가 된다 |
- 하위 분류는 그룹 페이지 상단의 배지로 고른다. 선택 상태는
?cat=로 주소에 남아 북마크·공유 후에도 복원된다. - PC 상단 메뉴와 모바일 사이드 메뉴의 동작은 동일하다.
- 그룹에 속하는 분류는
SubMenu의 링크가/category/{분류명}형태일 때만 인정한다. 외부 링크 등 다른 주소는 배지·글 모으기에서 제외된다.
드롭다운을 없앤 이유: 모바일에서는 상위 메뉴를 누르는 동작이 드롭다운 열기로 소모돼, 그룹 전체 글로 한 번에 갈 방법이 없었다.
10. 글 목록
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_POST_LIST_COVER | true | 목록에 글 커버 표시 |
LABOR_POST_LIST_COVER_HOVER_ENLARGE | false | 마우스 호버 시 커버 확대 |
LABOR_POST_LIST_COVER_DEFAULT | true | 커버 없으면 사이트 배경을 기본 커버로 |
LABOR_POST_LIST_SUMMARY | true | 글 요약 표시 |
LABOR_POST_LIST_PREVIEW | false | 목록에서 본문 미리보기 |
11. 글 상세
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_ARTICLE_ADJACENT | true | 이전/다음 글 추천 |
LABOR_ARTICLE_COPYRIGHT | true | 저작권 안내 |
LABOR_ARTICLE_NOT_BY_AI | false | '비AI 작성' 표시 |
LABOR_ARTICLE_RECOMMEND | true | 연관 글 추천 |
12. 위젯 (플로팅 버튼 등)
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_WIDGET_TO_COMMENT | true | 댓글 영역 이동 버튼 |
LABOR_WIDGET_DARK_MODE | true | 다크 모드 토글 |
LABOR_WIDGET_TOC | true | 모바일 플로팅 목차 |
13. 홈 v2 (새 홈 레이아웃)
LABOR_HOME_LAYOUT을 v2로 바꾸면 홈이 새 레이아웃으로 바뀝니다. 광고 줄, 검색창, 주제 메뉴바, 바로가기, 분류별로 둘러보기, 새로 올라온 글, 질의회시집, 바로 쓰는 서식 순서입니다. 홈에서는 기존 히어로와 오른쪽 사이드바가 사라집니다. 같은 스위치로 목록 페이지 메뉴바와 오른쪽 사이드바도 새 디자인으로 바뀝니다(아래 13.1). 문제가 있으면 v1으로 되돌리거나 행을 지우면 다음 재생성 때 기존 홈으로 돌아갑니다. 운영에 영향을 주지 않고 로컬에서만 보려면 .env.local에 LABOR_HOME_LAYOUT_LOCAL=v2를 넣습니다(개발 서버에서만 Notion 설정보다 우선, 운영 빌드에서는 무시). 명세는 specs/025-home-redesign-v2/에 있습니다.
| 설정명 | 값 예시 | 설명 |
|---|---|---|
LABOR_HOME_LAYOUT | v2 | v2면 새 홈, 그 외(기본 v1)는 기존 홈 |
LABOR_HOME_FEATURED_TAG | 필독 | 새로 올라온 글의 추천 카드에 쓸 태그(없으면 최신 글) |
LABOR_HOME_CALCULATORS | ["severance-pay","annual-leave"] | 바로가기 인기 계산기 slug 목록(모바일은 앞 6개). 없는 slug는 무시 |
LABOR_HOME_BROWSE_EXCLUDE | ["질의회시집","행정해석·판례","노동관계법령"] | 분류별로 둘러보기에서 뺄 그룹 |
LABOR_HOME_FORMS_GROUP | 서식·양식 | 바로 쓰는 서식 영역의 그룹 |
LABOR_HERO_CATEGORY_1 | {"title":"인사•노무 실무 계산","url":"/calculator"} | 바로가기 큰 칸(계산기 목록이 붙음) |
LABOR_HERO_CATEGORY_2~_5 | {"title":"질의회시집","url":"/group/질의회시집"} | 바로가기 작은 칸. /group/… 링크면 글 수가 자동으로 붙음. 선택 항목 desc(설명)·badge(배지)·count(false면 글 수를 붙이지 않음. 다른 값을 적으면 글 수를 그대로 보이고 서버 로그에 알림) |
LABOR_SEARCH_HELP_EXTRA | {"title":"전문가에게 문의","url":"https://…","desc":"…"} | 검색 첫 화면의 "글로 해결이 안 된다면"에만 둘 링크 하나. 바로가기에서 뺀 링크를 검색 화면에는 남길 때 쓴다. 그 칸의 끝자리를 먼저 잡고(한도 3), 바로가기에 같은 주소가 있으면 더하지 않는다 |
LABOR_AD_PRIMARY_URL | https://5240.cloud | 5240 배너 링크(새 탭) |
LABOR_AD_PRIMARY_BADGE | All-in-One HR시스템 | 5240 배너 배지(데스크톱) |
LABOR_AD_PRIMARY_TITLE_1 | 복잡한 인사, | 제목 윗줄 |
LABOR_AD_PRIMARY_HIGHLIGHT | 5240 | 큰 강조어(앞 두 글자 하늘색) |
LABOR_AD_PRIMARY_TITLE_2 | 으로 기준을 세우다 | 강조어 뒤 문구 |
LABOR_AD_PRIMARY_CTA | 문의 & 데모 신청 | 버튼 문구(데스크톱) |
LABOR_AD_SECONDARY_URL | https://okr.best/ | OKR.Best 배너 링크(새 탭) |
LABOR_AD_SECONDARY_TITLE | 대화가 곧 | 제목 앞부분 |
LABOR_AD_SECONDARY_HIGHLIGHT | 실행이다 | 파란색 강조 |
LABOR_AD_SECONDARY_DESC | 채널 · 보드 · AI를 하나로 연결한 업무 플랫폼 | 설명 |
LABOR_AD_SECONDARY_BADGE | 2026 무료 요금제 | 배지(데스크톱) |
- 광고 줄에는 5240·OKR.Best 두 배너만 둡니다. "누가 적임자인가"는 분류별로 둘러보기 바로 아래의 큰 배너로, 문구는 기존 설정
LABOR_SIDE_BANNER_TITLE/DESC/BADGE/URL을 그대로 씁니다(색과 장면은 광고주 사이트를 따라 남색 계열 — 데스크톱은 "둘씩 비교 → 순위" 두 단계 장면 포함, 모바일은 단순형). 전문가 모집 카드는 v2에서 쓰지 않습니다. - 분류 목록·순서·세부 주제는 Notion Menu/SubMenu 행 순서를 따르고, 글 수는 공개 글을 카테고리로 집계합니다(
/group페이지와 같은 기준). - JSON 값의 형식이 잘못되면 기본값을 쓰고 빌드/재생성 로그에
[home-v2]경고를 남깁니다. - 검증:
yarn test __tests__/lib/home __tests__/themes/labor/home __tests__/pages/indexHomeV2.test.js, v2로 띄운 서버에서yarn e2e:home(기본 대상http://localhost:3001,HOME_V2_URL로 변경).
13.1 목록 메뉴바·오른쪽 사이드바 v2
홈 v2와 같은 스위치(LABOR_HOME_LAYOUT, 로컬은 LABOR_HOME_LAYOUT_LOCAL)로 켜집니다. 새 Notion 설정값은 없습니다. 명세는 specs/026-list-sidebar-v2/에 있습니다.
- 스위치 전달:
fetchGlobalAllData가 캐시에서 꺼낸 사본에props.layout(v1/v2)을 붙입니다. 캐시 원본에는 넣지 않으므로 로컬 스위치를 바꾸면 새로고침 한 번으로 반영됩니다. 화면은isLayoutV2(props)(layout === 'v2'또는props.home)로 판단합니다. - 목록 메뉴바: 그룹·분류·태그 목록과 아카이브에서 옛 메뉴바 대신 홈 주제 메뉴바와 같은 얇은 메뉴바를 씁니다. Notion Menu 행 순서(옛 메뉴바의 가나다순이 아님), 분류 링크가 없는 메뉴 행은 제외, 현재 그룹은 파란 글자·밑줄·
aria-current, 하위 메뉴 드롭다운 없음. 좁은 화면에서는 한 줄 가로 스크롤이고 현재 그룹을 가운데로 옮깁니다. 검색 결과 페이지(사이트 전체·그룹·분류 범위 검색)는 원래 메뉴바가 없어 그대로입니다. 홈 v2 데이터 계산이 실패해 기존 홈으로 돌아가면props.layout도v1로 되돌려 화면이 섞이지 않습니다. - 글 카드: 그룹·분류·태그 목록과 검색 결과의 글 카드를 넓은 화면 2열·좁은 화면 1열 격자로 보여 줍니다(
components/side/PostCardGrid.js). 카드는 분류 배지(왼쪽 위)·발행월(오른쪽 위) → 제목 → 요약 두 줄이고, 옛 카드의 액센트 바·아이콘 칩·점선과 태그는 두지 않습니다. 카드의 배지와 마우스 올림 색은 그 글이 속한 그룹의 색입니다. - 폴더 탭 메뉴와 그룹 판: 목록 페이지의 메뉴바는 폴더 탭 모양입니다(
components/side/ListMenuBar.js). 현재 그룹이 탭으로 솟아 바로 아래 판(components/side/GroupPanel.js)과 같은 옅은 그룹 색 바탕으로 이어지고, 판 안에 그룹 이름·글 개수·범위 내 검색·분류 배지가 들어갑니다. 메뉴는 「주제」와 「자료」로 나뉘며, 자료 그룹은LABOR_MENU_RESOURCE_GROUPS로 정합니다(Notion 설정으로 덮어쓸 때는 JSON 배열 문자열).LABOR_MENU_HIDDEN_GROUPS(기본['질의회시집'])에 적은 그룹은 목록 메뉴바·홈 주제 메뉴바·휴대폰 메뉴(MenuListSide)·v1 상단 메뉴(MenuListTop)에서 빠지고, 그 그룹 안에 있을 때만 목록 메뉴바와 v1 상단 메뉴에 보입니다(lib/listMenu.js의visibleMenuGroups,lib/qnaArchiveGroup.js의hiddenMenuGroupsOf). 홈 바로가기 타일은 그대로입니다. Notion의 메뉴 행은 숨기지 않습니다 — 메뉴가 질의회시 글을 가리는 기준이라(전용 화면, 조문 링크, 법령 카드의 질의회시 수) 행을 숨기면 그것들이 함께 꺼집니다. 탭·판·카드가 쓰는 색 식은lib/folderTheme.js한 곳에 있습니다. 홈의 주제 메뉴바는 폴더 탭이 아닌 얇은 줄 모양 그대로입니다. - 분류 배지 한 줄: 분류 배지는 한 줄을 넘기지 않고 넘치는 배지를 「더보기 +N」으로 접습니다(
lib/useBadgeFit.js, sm 미만에서는 가로로 밉니다). 한 그룹의 분류가 모두 같은 낱말로 끝나면(질의회시집) 배지에서만 그 낱말을 뺍니다. - 질의회시집 문답 목록: 질의회시집(
listView)은 표 대신 제목 전체와 회시 요약 두 줄을 적은 목록으로 보여 줍니다(components/side/QnaPostList.js). 날짜로 묶지 않고 항목마다 해까지 적습니다. 옛 표(BlogPostListRow)는 v1 목록과 홈이 계속 씁니다. - 질의회시 글 화면: 질의회시집 글은 일반 글 틀 대신 제목 → 출처 줄(문서번호·회시일·수록 발간물) → 요지 → 질의 칸 → 회시 칸 순서로 보여 줍니다(
components/qna/QnaPostView.js, 스펙specs/027-qna-post-view). 본문 문장은 고치지 않고, 블록을 「개요 / 질의 / 회시」 제목 기준으로 나눠(lib/qnaPost.js) 조각마다 기존 Notion 렌더러로 그립니다(components/qna/QnaSlice.js). 질의와 회시의 번호 표기((질의1)·<질의1>, 회시 쪽은(답변1)·(회시1)도)가 1부터 같은 개수로 정확히 일치하면 번호별로 짝짓습니다. 「회시」 제목이 늦게 놓여 답 일부가 질의 구역에 들어간 글은, 질의 번호가 다시 1로 시작하거나 답 쪽 표기가 나오는 자리부터 회시로 보고 번호가 맞을 때만 그렇게 묶습니다. 짝짓지 못한 글은 질의 한 칸·회시 한 칸이되 칸 안에서 번호마다 끊어 보여 줍니다. 이름표(「질의 1」「회시」「상황」)가 대신하는 자리에서는 문단 맨 앞의 표기 글자((질의1)등)를 화면에서만 숨기고 Notion 글에는 그대로 둡니다. 이 화면에서는 큰 머리 영역·단어 수·읽는 시간·공유 버튼 묶음·목차를 그리지 않습니다. 「질의」「회시」 제목이 없거나 다른 제목이 섞인 글, 비밀번호가 걸린 글, 다른 그룹의 글은 기존 화면 그대로입니다. 공용components/NotionPage.js에는 본문 영역 id를 뺄 수 있는articleIdprop을 더했습니다(기본값은 종전과 같음). - 질의회시 글 화면 다듬기: 요지는 분류 색과 무관하게 옅은 노랑 바탕에 주황 왼쪽 띠로 보여 줍니다. 한 문단에
• (상황) … • (질의1) … • (질의2) …가 이어진 글은글머리 기호 + 표기앞에서 끊은 조각 문단으로 바꾼 본문 사본을 만들어(lib/qnaInlineMarks.js) 상황과 질의가 따로 보이게 합니다. 문장은 고치지 않고, 끊은 자리의 글머리 기호만 화면에서 뺍니다. 끊은 결과가 어긋나면(번호가 1부터 차례가 아님, 빈 조각, 링크 가운데 등) 그 구역은 끊지 않습니다. 상황은 질의와 회시의 번호가 짝지어지지 않은 글에서도 따로 뗍니다. 글머리 점 목록은 점 대신 왼쪽 띠로 표시하고(목록 요소는 그대로; 회시 칸의 띠는 파랑, 질의·상황 칸의 띠는 회색 — 회시 칸에 붙인qna-answer안에서만 색이 바뀝니다), 본문은 보통 굵기(400)·줄 간격 1.8로 보여 줍니다. 색·띠·굵기는 화면 뿌리의.qna-post안에서만 듣습니다(style.js). 확인은yarn e2e:qna-post(조회·방문 집계 요청을 가로챔)와node scripts/qna-inline-census.mjs(전체 글 조사, 읽기 전용)로 합니다. 스펙은specs/031-qna-post-readability에 있습니다. - 오른쪽 사이드바: 목차(글 상세) → 새로 올라온 글 5개 → 많이 찾는 주제(태그 12개 + 최근 7일 일자별 방문 막대)입니다. 전문가 모집·누가 적임자인가 카드는 사이드바에 두지 않고 홈에서는 분류별로 둘러보기 아래 배너(적임자)로만 보입니다. 목차가 있는 글 상세에서는 목차만 스크롤을 따라오고, 그 밖의 화면에서는 뒤의 두 카드가 따라옵니다. 글 수·개설 일수 요약 줄과 페이스북·옛 정보 카드는 v2에서 빠집니다.
- 검색 첫 화면(
/search, 검색어 없음): 분류 전체·태그 전체(2,500여 개)를 펼치던 화면 대신 ① 큰 검색창(자동 포커스)과 많이 찾는 주제 10개 + "전체 태그 →" ② 주제(그룹) 카드 — 그룹명·글 수·글 많은 분류 3개, 어느 그룹에도 없는 분류는 "그 밖의 분류" ③ "글로 해결이 안 된다면" —LABOR_HERO_CATEGORY_1~5와LABOR_SEARCH_HELP_EXTRA(같은 주소는 한 번만) 중 글 목록(/group·/category·/tag)이 아닌 링크 최대 3개(제목·desc)를 보여 줍니다. 이 화면에서는 사이드바의 "많이 찾는 주제" 카드를 빼고, 페이지에 싣는 태그도 상한(LABOR_TAG_OPTIONS_LIMIT)까지만 보냅니다. 검색 결과 화면은 그대로입니다. - 검증:
yarn test __tests__/lib/home/layoutProp __tests__/lib/db/globalDataLayout __tests__/themes/labor/side __tests__/themes/labor/lib/listMenu, v2로 띄운 서버에서yarn e2e:list(기본 대상http://localhost:3001,LIST_V2_URL로 변경).
13.2 법령 글 화면과 분류 화면
「노동관계법령」 메뉴의 글(법률·시행령·시행규칙 원문, 3단비교)을 위한 화면입니다. 글은 scripts/law-library/ 도구가 올립니다(쓰는 법은 그 폴더의 README.md). 명세는 specs/032-labor-law-library/에 있습니다.
| 설정명 | 기본값 | 설명 |
|---|---|---|
LABOR_LIST_VIEW_GROUPS | ['질의회시집', '노동관계법령'] | 카드 격자 대신 목록형(한 쪽 20편)으로 보이는 메뉴. 전에는 질의회시집 하나가 코드에 박혀 있었습니다 |
LABOR_MENU_RESOURCE_GROUPS | […, '노동관계법령', …] | 목록 메뉴바의 「자료」 묶음에 노동관계법령을 더했습니다 |
- 법령 글의 판정: 옛 주소 별칭(
legacySlug)이<접두>/law/로 시작하는 글입니다(lib/utils/lawPosts.js). 끝 조각이시행령·시행규칙·3단비교면 그 종류, 없으면 법률입니다. 비밀번호가 걸린 글과 첫 블록이 문단(정보 줄)이 아닌 글은 일반 글 화면 그대로입니다. 글의 종류에 맞는 화면은components/PostBody.js가 고릅니다(질의회시 → 법령 → 일반). - 화면의 구성: 큰 머리 영역 대신 분류 → 제목(
h1하나) → 정보 줄(공포번호·시행일·기준일·원문 링크) → 안내 → 최근 개정 → 이 법령을 다룬 질의회시 → 조문 목차 → 본문 → 끝(부칙·별표·서식 안내, 주소 복사) 순서입니다(components/law/LawPostView.js). 본문 문장은 고치지 않고 기존 Notion 렌더러 하나가 그립니다. 정보 줄·끝 문단·「최근 개정」의 칸·3단비교의 칸처럼 짧은 블록 묶음은components/law/LawBlocks.js가 가볍게 바로 그립니다(문단·호·목·인용·자식 없는 콜아웃과 굵게·취소선·밑줄·색·링크만. 그 밖의 꼴이 섞이면 기존 렌더러에 맡김) — 칸마다 렌더러를 두면 3단비교 글에서 수백 개가 되어 렌더가 초 단위로 걸리고<main>이 겹칩니다. 본문의 호·목(글머리 목록 항목)은 화면에서만 표시가 붙은 문단으로 바꿉니다(lib/lawPostFlat.js) — 렌더러가 목록 항목을 그릴 때마다 본문의 블록 전체를 훑어, 블록 700개인 글에서 렌더 시간의 4분의 3을 차지했습니다. 스타일은lawStyle.js에 있고 화면 뿌리의.law-post안에서만 듣습니다. - 조문 목차: 글 안의 「조문 목차」(
components/law/LawToc.js, 열려 있고 장마다 묶임 — 장은 접힘)와 기존 목차(사이드바, 떠 있는 버튼) 두 곳에 같은 목차가 나옵니다. 기존 목차에는 글의toc를 장·절·조문의 것으로 바꾼 사본을 넘깁니다(lib/useLawPost.js). 글 안의 목차 링크는 조문 번호로 된 자리 주소(#제60조,#제76조의2)라서 글을 새 판으로 갈아도 그대로이고, 그 주소로 열면 그 조문으로 갑니다. 조문 제목의 번호 글자는 그 조문의 자리입니다 — HTML에id="제60조"로 들어 있어 주소의#제60조가 스크립트 없이도 그 조문을 가리킵니다(검색 엔진, 밖에서 거는 링크). 본문 렌더러가 제목의 자리 이름을 블록 id로만 만들기 때문에, 번호 글자를 제 자리로 가는 링크 꾸밈으로 감싸고(lib/lawPostAnchors.js, 날짜를 보는 단계 앞뒤로 붙임) 법령 본문 안에서만 공용 링크 부품(components/NotionLink.js의SelfAnchorScope)이 그것을 링크가 아니라 자리(<span id>)로 그립니다. 다른 글의 링크는 그대로입니다. 렌더러가 제목마다 두는 고리(블록 id 주소)는 법령 본문에서 숨깁니다. 블록 id 자체를 조문 번호로 바꾸면 안 됩니다 — 렌더러가 블록 id로 들여쓰기 값을 기억해 두어, 법령마다 같은 이름을 쓰면 서버와 브라우저의 그림이 어긋납니다. 글 안의 조문 목차 위에는 찾기 칸이 늘 보입니다(components/law/LawTocSearch.js, 찾는 규칙은lib/lawPostSearch.js) — 조문 번호(60,제76조의2)나 제목의 낱말(연차, 띄어쓰기 무시)을 치면 맞는 조문이 나오고, 누르거나 Enter를 치면 그 조문으로 갑니다. - 날짜로 바뀌는 표시: Notion에는 날짜만 올라 있고 「시행 예정」「부터 시행」은 글을 여는 날의 한국 날짜로 화면이 붙입니다. 글자가
[시행일로 시작하는 인용은 현행 조문에 붙은 표시(자리는 그대로, 이름표만), 콜아웃은 시행 예정판입니다. 시행 예정판은 바뀌는 날(그 판의 표시 가운데 앞 판에 없는 줄의 날짜) 전에는 현행 조문 아래의 칸으로, 그날부터는 조문 제목 바로 아래의 본문으로 보이고 옛 글은 「…까지의 조문」으로 접힙니다. 조문이나 장의 제목이 바뀌는 판이면 제목의 글자도 그날 바뀝니다. 이 일은 받은 본문을 바꾸지 않고 사본을 만들어 합니다(lib/lawPostBody.js, 읽기는lib/lawPost.js·lib/lawMarks.js). 서버가 그린 날을 화면 뿌리의data-law-today에 적어 두어 브라우저가 같은 날로 이어받고, 이어받은 뒤와 화면 안에서 다른 글로 옮겨 갈 때 오늘로 다시 판단합니다(lib/useLawPost.js). 한 번 본 날보다 뒤로는 가지 않습니다(브라우저의 시계가 뒤져 있어도 서버가 올린 글이 내려가지 않게). - 최근 개정: 제목이
최근 개정으로 시작하는 첫 토글을 본문에서 떼어 글 머리 아래에 구역으로 그립니다(components/law/LawAmendments.js, 읽기는lib/lawPostAmendments.js). 처음에는 요약 세 줄만 보입니다 — 어느 개정인지, 바뀐 조문의 수와 시행일마다의 수(바뀐 조문 14개 — 시행 중 2 · 2026. 12. 8. 시행 11 · 2027. 1. 1. 시행 2—lib/lawPostAmendments.js의amendmentSchedule이 센다), 「바뀐 내용 보기」. 조문의 번호는 요약에 늘어놓지 않습니다(번호만으로는 무엇이 바뀌었는지 알 수 없음). 바뀐 글은 「바뀐 내용 보기」로 펼칩니다(접혀 있어도 HTML에 있음) — 펼친 채로 두면 조문 목차가 화면 두세 개 아래로 밀립니다. 펼치면 바로 바뀐 조문마다 종류(신설·삭제·변경)와 시행 이름표, 「개정 전」「개정 후」 칸의 한 쌍이 나오고(칸의 이름은 화면이 적습니다 — 글 저장소의 칸 첫 줄에는 올릴 때의 말이 있고, 2026-10-13 전에 올린 글의 「바뀌기 전」「바뀐 뒤」도 읽어 새 말로 바꿔 그립니다:lib/lawPostAmendments.js의relabelAmendments), 폭 768px 이상에서는 좌우로 나란히, 그보다 좁으면 위아래로 놓입니다(신설·삭제처럼 한쪽만 있으면 한 줄을 다 씀). 일부만 바뀐 칸의 바뀐 글자는 전은 취소선과 빨강, 후는 굵은 글자와 노란 형광펜 바탕입니다(글자색은 본문과 같음) — 글 저장소에는 밑줄로 올라가 있지만 밑줄 친 파란 글자는 링크로 보여 화면이 바꿔 그립니다. 칸의 글자 가운데 90% 이상이 바뀐 글자면 통째로 바뀐 칸으로 보고(data-law-whole) 글자마다의 표시를 뺍니다. 시행 이름표는 두 가지입니다 — 날짜가 붙어 올라온 조문(시행 예정)은 여는 날로 판단해 「시행 예정」이나 「부터 시행 중」을 붙이고, 날짜 없이시행 중으로 올라온 조문(올릴 때 이미 현행 글에 들어 있던 개정)은 날짜 없이 「시행 중」이라고만 적습니다. 약속과 다른 블록이 있으면 풀지 않고 토글 그대로 보입니다. 칸은 인용이 아니라 콜아웃입니다 — 공용 인용 부품(components/NotionPage.js의NotionQuote)은 밑줄과 색을 그리지 않습니다. - 3단비교 글: 같은 화면이고 본문이 단 묶음(법률·시행령·시행규칙)의 줄들입니다. 줄과 칸은 화면이 제 틀에 놓습니다(
components/law/LawCompare.js, 읽기는lib/lawPostCompare.js). 641px 이상은 같은 너비의 칸으로 나란히, 그 아래는 줄 하나가 카드 하나로 쌓이며 「해당 조문 없음」뿐인 단은 좁은 화면에서 뺍니다. 칸의 글자가 400자를 넘으면 넘기 전까지의 문단만 보이고 나머지는 「펼치기」로 접습니다 — 접는 자리는 Notion에 올리지 않고 화면이 정합니다(단 안의 토글은 한 요청에 올릴 수 있는 중첩을 넘습니다). 약속과 다른 글(줄이 없다, 이름표 없는 단, 줄이 아닌 블록이 섞임)은 풀지 않고 올라온 그대로 그립니다. - 목록: 「노동관계법령」 메뉴 화면(
/group/노동관계법령)은 법령마다 카드 하나입니다(components/law/LawCardGrid.js) — 정식 이름(누르면 법률 원문), 분류 이름이 다르면 그 위에 작게, 법률의 공포번호·시행일, 「질의회시 N편」 알약, 있는 문서의 버튼(법률·시행령·시행규칙·3단비교). 법령 글만으로 된 그룹이면 서버(pages/group/[group]/index.js)가lib/lawCards.js의lawCardsOf로 카드(props.lawCards)를 만들고 글의 목록은 비워 넘깁니다. 질의회시 수는lib/db/SiteDataApi.js가 다시 내보내는lawQnaCountsOf(lib/law-links/forPost.js)가 법령 글 화면의 조문 목록과 같은 기준으로 셉니다(여러 조문에 걸친 글은 한 번). 쪽을 나누지 않고 옛 쪽 주소는 첫 화면으로 임시로 보냅니다(307). 판 머리는법령 N개 · 글 M개이고 분류 배지를 두지 않습니다(GroupPanel의lawCount). 분류 화면은 목록형이고, 차례는 법령은 메뉴에 놓인 순서·한 법령 안에서는 법률 → 시행령 → 시행규칙 → 3단비교입니다(lib/lawOrder.js, 분류·그룹 목록의 네 페이지에서 정렬). 법령 글은 「새로 올라온 글」과 RSS에 나오지 않습니다 — 새 글을 고르는 세 길(사이드바의getLatestPosts, 홈 v2의lib/home/buildHomeData.js,pages/api/rss.js)이 같은 판정(lib/utils/latestPosts.js의isFeedPost)을 씁니다. - 분류 화면의 정확 일치: 분류 화면·쪽 넘김·분류 안 검색은 이름이 정확히 같은 분류의 글만 보입니다(
lib/utils/categoryMatch.js). 전에는 이름이 들어 있기만 하면 같이 나와/category/임금에 「최저임금」「임금채권보장법 질의회시」의 글이,/category/퇴직금에 「퇴직금 기초상식」의 글이 섞였습니다. 법령 분류(근로기준법)와 질의회시 분류(근로기준법 질의회시)가 섞이지 않게 하려고 고쳤습니다. - 검증:
yarn test __tests__/themes/labor/law __tests__/themes/labor/lib/lawPost __tests__/themes/labor/lib/useLawPost.test.js __tests__/pages/categoryExactMatch.test.js __tests__/pages/lawListOrder.test.js __tests__/pages/groupLawCards.test.js __tests__/themes/labor/lib/lawCards.test.js __tests__/lib/law-links/forPost.counts.test.js, 글이 올라간 뒤yarn e2e:law-post(기본 대상http://localhost:3001,LAW_POST_URL로 변경. 조회·방문 집계 요청을 가로챕니다).
13.3 조문과 질의회시 잇기
질의회시 글의 「법령 이름이 붙은 조문 인용」과 법령 원문 글의 조문을 서로 잇습니다. 명세는 specs/033-law-qna-links/, 색인을 만드는 도구는 scripts/law-qna-links/(쓰는 법은 그 폴더의 README.md)에 있습니다. 새 설정값은 없습니다.
- 무엇을 잇나: 조건을 모두 채운 인용만 잇습니다 — ① 사이트에 올린 법령의 정식 이름이 조문 번호 바로 앞에 붙어 있다(「근로기준법」 제60조. 꺾쇠는 반각 「」도 받고 띄어쓰기와 가운뎃점의 꼴은 가리지 않습니다. 약칭·「법 제60조」·「구 근로기준법」은 아니고, 「제43조의 2항」은 제43조로 읽습니다) ② 그 글의 회시일이 그 법령의 기준일(마지막 전부개정이나 제정이 시행된 날)과 같거나 뒤다 — 사람이 확인한 기준일만 씁니다 ③ 지금 원문에 그 번호의 조문이 있다 ④ 그 조문의 번호가 일부개정으로 옮겨졌으면(원문의 「이동」 표시) 옮긴 개정이 시행된 뒤의 글이다 ⑤ 그 법령 글이 공개되어 있다. 규칙은
lib/law-links/citations.js하나에 있고 도구·서버·화면이 같이 씁니다. - 이을지는 서버가 정합니다: 글의 자료를 만드는 자리(
lib/db/SiteDataApi.js의resolvePostProps)가lib/law-links/forPost.js를 한 번 부릅니다. 질의회시 글(분류가 메뉴의 「질의회시집」 묶음에 든 글)에는post.lawLinks(법령·종류 → 그 글의 주소와 이을 조문 번호들), 공개된 법령 원문 글(옛 주소 별칭이 정확히<접두>/law/<분류>[/시행령|시행규칙]인 글 — 새 판으로 가는 도중의 임시 글…~next와 3단비교 글은 아님)에는post.lawQna(조문 번호 → 그 조문을 다룬 질의회시들: 주소·제목·회시일·amendedAfter)를 붙입니다. 그 밖의 글에는 아무것도 붙지 않습니다. 붙이다 실패하면 글은 링크와 목록 없이 그대로 열립니다. 법령 글이 「검수」인 동안과 기준일을 확인하기 전에는 아무것도 붙지 않습니다 — 공개 전의 글을 공개된 것처럼 보이게 하는 스위치는 없습니다. - 질의회시 글: 본문의 인용 글자(여는 꺾쇠나 이름부터 조문 번호까지)가
<법령 글의 주소>#제60조로 가는 링크가 됩니다(lib/qnaLawLinks.js, 테마의 들머리에서lib/useLawLinkedPost.js로 한 번). 본문 사본의 글자 조각에 링크 꾸밈만 더하고 글자는 바꾸지 않습니다. 조각 나누기(묻고 답하기 화면 —specs/031) 앞에 붙여, 나누지 못해 일반 본문으로 그리는 질의회시 글에도 같은 링크가 보입니다. 질의회시 화면 안의 링크는 흐리지 않게(본문과 같은 진하기) 밑줄로 가릅니다(style.js의.qna-post .notion-link). - 법령 글의 모음 칸: 조문 목차 위에 「이 법령을 다룬 질의회시」 칸이 있습니다(
components/law/LawQnaSummary.js, 고르기는lib/lawPostQna.js의qnaSummaryOf). 질의회시가 있는 조문을 많은 차례로 8개까지 보이고 나머지는 「조문 N개 더 보기」에 접습니다. 누르면 그 조문 아래의 줄을 열어 그리로 갑니다(스크립트가 없으면 링크가 조문의 자리 주소라 그 조문으로 갑니다). 3단비교 글에는 없습니다. - 법령 글: 조문 아래에 「이 조문을 다룬 질의회시 N건」이 접혀 있습니다(연한 파랑 상자·말풍선·건수 알약으로 눈에 띄게 —
lib/lawPostQna.js, 본문 사본 단계의 끝 —lib/useLawPost.js). 본문 사본에 토글 블록을 끼우는 방식이고(id가qna~로 시작), 그 글에 실제로 있는 조문에만 끼웁니다. 펼치면 제목(링크)·회시일이 최신순으로 나오고, 10건이 넘으면 나머지는 안쪽의 「더 보기 N건」에 있습니다. 회시된 뒤에 그 조문이 개정된 글에는 「회시 뒤 개정」이라고 적고 목록의 첫 줄에 그 뜻을 한 번 적습니다. 조문 목차와 조문의 자리 주소는 그대로이고, 3단비교 글에는 끼우지 않습니다. 스타일은lawStyle.js의.notion-toggle[class*='notion-block-qna~']. - 생성 파일:
lib/law-links/data/의baselines.json(기준일 — 도구가 후보를 쓰고 사람이confirmed를 바꿈),articles.json(지금 원문에 있는 조문, 마지막 개정일, 번호가 지금의 뜻이 된 날),index.json(조문 → 그 조문을 인용한 글들 — 공개된 글의 id와 회시일만 적고, 주소·제목·회시일은 화면이 그릴 때 글 목록의 지금 값에서 가져옵니다). 조문 목록의 표시는 032가 읽어 둔 원문과 원문 PDF의 본문, 두 곳에서 읽습니다(한 문단 조문의 대괄호 표시가 앞의 것에 없음). 이 파일들을 들여오는 것은forPost.js하나뿐입니다 — 화면 쪽 코드가 들여오면 색인이 브라우저 묶음에 듭니다(__tests__/lib/law-links/clientBundle.test.js가 지킵니다). 법령을 새 판으로 갈 때는 도구의--baselines와--articles를 먼저 돌립니다(law-library --load가 조문 목록이 옛 판의 것이면 알립니다). - 읽고 뺀 인용:
lib/law-links/data/exclusions.json은 사람이 쓰는 목록입니다(글의 id,법령|종류|조문, 뺀 까닭). 조건을 다 채웠는데도 글이 옛 번호로 적어 다른 조문에 닿는 쌍을 여기에 적으면 서버(링크와 목록)와 도구(색인)가 그 쌍을 잇지 않습니다. 이름과 조문 사이에 줄임말을 정하는 괄호(「근로복지기본법」(이하 ʻ법ʼ) 제62조)가 낀 인용은 잇습니다. - 검증:
yarn test __tests__/lib/law-links __tests__/scripts/law-qna-links __tests__/themes/labor/lib/qnaLawLinks.test.js __tests__/themes/labor/lib/lawPostQna.test.js,yarn e2e:law-qna-links(기본 대상http://localhost:3001,LAW_QNA_LINKS_URL로 변경. 조회·방문 집계 요청을 가로챕니다. 법령 글이 공개되기 전에는 「그려지지 않는다」만 보고 나머지는 건너뜁니다).
13.4 운영자 이용 분석 (/manage/insights)
운영자가 어떤 글·분야·메뉴가 많이 쓰이는지, 글에서 글로 어떻게 옮겨 가는지, 어디서 들어와 무엇을 찾는지 묶음 통계로 봅니다. 명세는 specs/040-admin-insights/에 있습니다. 외부 분석 도구는 쓰지 않습니다(Umami 무료 요금제에 API가 없어서) — 모든 숫자를 지금 쓰는 Redis에 직접 쌓습니다.
- 들어가기: 환경변수
ANALYTICS_ADMIN_PASSWORD(서버 전용,.env.example참고)를 맞히면 7일짜리 로그인 쿠키(insights_admin, HttpOnly·SameSite=Strict, 운영에서는 Secure)를 받습니다. 쿠키 서명의 열쇠는 서버 비밀(NEXTAUTH_SECRET)과 비밀번호로 만들어, 비밀번호를 바꾸면 이전 쿠키가 모두 무효가 되고 쿠키만으로는 비밀번호를 대입해 볼 수 없습니다. 비밀번호가 비거나 운영에서NEXTAUTH_SECRET이 없으면 아무도 못 들어갑니다. 같은 주소에서 10분에 5번까지 시도하고, 횟수를 셀 Redis가 닿지 않으면 로그인을 막습니다(503). 로그인·로그아웃은 이 사이트의 화면에서 보낸 요청만 받습니다. 지식게시판 운영 토큰(KB_ADMIN_TOKEN)과는 따로입니다. 페이지는 noindex이고 robots.txt에서/manage/를 막습니다. - 쌓는 것(
lib/insights/record.js, KST 하루마다 해시 네 개, 92일 뒤 만료): 글 조회·기능 단위 방문·기기·처음 들어온 곳·계산기 사용(ins:<날짜>:c), 글로 옮겨 간 이동과 그 수단(:mv), 검색어와 결과 0건 여부(:q), 404 주소와 어디서 왔나(:nf). 운영이 아니면 키 앞에dev:가 붙어 로컬 기록이 운영 숫자에 섞이지 않습니다. 주소·브라우저 식별값·한 사람의 경로는 남기지 않습니다. 봇, 이 사이트의 화면이 보냈다는 표시(Sec-Fetch-Site/Origin)가 없는 요청, 로그인한 운영자의 열람, 한 주소가 1분에 60번 넘게 보낸 요청(서버 인스턴스 메모리에서 셈)은 쌓지 않고, 글 조회는 지금 글 목록에 있는 글일 때만 쌓습니다(lib/insights/requestFilter.js,lib/insights/fromResolver.js). - 요청은 화면 하나에 하나: 글 화면은 지금의 조회수 요청(
components/ViewCounter.js→/api/views)에 바로 전 경로·누른 링크의 묶음·첫 화면의 referrer를 실어 보냅니다. 누적 조회수(post:views:*)는 지금처럼 오릅니다. 글이 아닌 화면은_app의components/InsightsTracker.js가/api/insights/hit을 하나 보냅니다(검색 화면은 결과 수를 받은 뒤). 계산기는 처음 값을 넣을 때 한 번 더 보냅니다(CalculatorShell). 화면만 바꾸는 이동이라 바로 전 경로는lib/insights/navMemory.js가 기억합니다. - 「어떻게 옮겨 갔나」 표시: 링크를 감싸는 묶음에
data-insights-via(body·list·related·menu·more)를 붙입니다. 규칙과 붙인 자리는specs/040-admin-insights/contracts/nav-attributes.md, 지키는 테스트는__tests__/themes/labor/insights/navAttributes.test.js입니다. 새 링크 묶음을 만들면 맞는 값을 붙입니다. 질의회시 「더 볼 것」 상자(QnaMoreLinks)에는data-insights-via='more'를 붙여야 그 클릭이 셉니다 — 이 기능이 들어갈 때 그 상자는 다른 브랜치에 있었습니다. - 알아 둘 것: 날짜별 기록은 이 기능을 내보낸 날부터 쌓입니다(그 전 기간은 칸에 「○월 ○일부터 쌓인 기록」). 머문 시간은 재지 않습니다. 열람 한 번에 Redis 명령이 4~5개 늘어나므로 Upstash 요금제 한도를 함께 봅니다.
- 검증:
npx jest __tests__/lib/insights __tests__/pages/api/insights __tests__/pages/api/manage __tests__/pages/api/views.test.js __tests__/pages/manage __tests__/components/InsightsTracker.test.js __tests__/components/ViewCounter.test.js __tests__/themes/labor/insights, 손 확인은specs/040-admin-insights/quickstart.md.
⚠️ 코드 미참조 키 (설정해도 효과 없음)
config.js에 정의되어 있으나 현재 테마 코드가 읽지 않는 키입니다. Notion에 넣어도 동작하지 않습니다.
| 설정명 | 비고 |
|---|---|
LABOR_HOME_BANNER_ENABLE | 히어로 표시는 홈 라우트(/)로만 결정 |
LABOR_POST_LIST_IMG_CROSSOVER | 목록 이미지 좌우 교차(미구현) |
LABOR_WIDGET_LATEST_POSTS | 최신 글 카드(항상 표시) |
LABOR_WIDGET_ANALYTICS | 통계 카드(미사용) |
LABOR_WIDGET_TO_TOP | 맨 위로 버튼(미참조) |
참고:
config.js의 "아래 설정은 무효, 개발용 예약일 뿐" 주석은 부정확합니다. 9~12절의LABOR_MENU_*·LABOR_POST_LIST_*·LABOR_ARTICLE_*·일부LABOR_WIDGET_*키는 실제로 코드가 사용합니다(위 5개만 진짜 미참조).
