Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
name: CI

# PR 에서는 배포하지 않고 검증만 한다. 깨진 채로 main 에 들어가지 않게 하는 것이 목적이다.
on:
pull_request:
branches: [main]

jobs:
verify:
name: 빌드 · 타입 · 린트 · 화면 검증
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: 의존성 설치
run: npm ci

- name: 타입 검사
run: npm run typecheck

- name: 린트
run: npx eslint src scripts

- name: 빌드
run: npm run build

# 이전에 손으로 잡던 것들이다. /data 표가 그리드 칸을 밀어 가로로 넘쳤고,
# 표에 caption 이 없어 스크린리더가 무슨 표인지 알려 주지 못했다.
- name: 화면 검증 (반응형 · 접근성)
run: |
npx serve out -p 4173 -L &
npx wait-on http://localhost:4173 --timeout 60000
node scripts/verify-pages.mjs http://localhost:4173
93 changes: 93 additions & 0 deletions .github/workflows/deploy.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
name: Deploy

on:
push:
branches: [main]
# 사이트에 적힌 수집량은 백엔드가 매주 새로 받아 온다. 다시 빌드해야 반영된다.
schedule:
- cron: '0 20 * * 1' # 매주 화요일 05:00 KST (어린이집·유치원 동기화 이후)
workflow_dispatch:

# 배포는 한 번에 하나만. 뒤에 들어온 것이 이기게 두면 낡은 빌드가 최종본이 될 수 있다.
concurrency:
group: pages
cancel-in-progress: false

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
name: 정적 파일 생성
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: 의존성 설치
run: npm ci

- uses: actions/configure-pages@v5
id: pages

- name: 빌드
# 프로젝트 페이지는 /<저장소명> 아래에 붙는다. basePath 가 없으면 CSS·JS 가 전부 404 다.
env:
NEXT_PUBLIC_BASE_PATH: ${{ steps.pages.outputs.base_path }}
NEXT_PUBLIC_SITE_URL: ${{ steps.pages.outputs.base_url }}
run: npm run build

- uses: actions/upload-pages-artifact@v3
with:
path: out

deploy:
name: Pages 배포
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment

smoke:
name: 배포 후 확인
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# 배포가 성공했다는 것과 사이트가 실제로 뜬다는 것은 다르다.
# 파일이 올라갔는데 basePath 가 어긋나 CSS 가 404 인 경우가 대표적이다.
- name: 페이지 응답 확인
env:
SITE: ${{ needs.deploy.outputs.page_url || format('https://{0}.github.io/{1}/', github.repository_owner, github.event.repository.name) }}
run: |
base="${SITE%/}"
fail=0
for path in "/" "/benefits/" "/facilities/" "/data/" "/trust/" "/sitemap.xml" "/robots.txt"; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --retry 5 --retry-delay 5 --retry-all-errors "$base$path")
printf ' %-16s %s\n' "$path" "$code"
[ "$code" = "200" ] || fail=1
done

# 스타일이 붙었는지까지 봐야 의미가 있다. basePath 가 어긋나면 여기서 걸린다.
css=$(curl -sS "$base/" | grep -oE '/[^"]*\.css' | head -1)
if [ -n "$css" ]; then
code=$(curl -sS -o /dev/null -w '%{http_code}' "$base$css")
printf ' %-16s %s\n' "css" "$code"
[ "$code" = "200" ] || fail=1
else
echo " css 링크를 찾지 못했습니다"
fail=1
fi

exit $fail
77 changes: 76 additions & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,82 @@ npm run dev # http://localhost:3000
npm run build # out/ 에 정적 파일 생성
```

서버가 할 일이 없어 `output: 'export'` 로 정적 출력만 만듭니다.
서버가 할 일이 없어 `output: 'export'` 로 정적 출력만 만듭니다. `out/` 를 그대로 어디든
올리면 됩니다.

## 페이지

| 경로 | 내용 |
|------|------|
| `/` | 개요. 월령 축을 옮기면 지원금이 유효·소급가능·지남으로 갈립니다 |
| `/benefits` | 자격 판정, 수령액 계산, 금액 신뢰도 |
| `/facilities` | 정원 관측, 빈자리 알림, 대기 기간 통계 |
| `/data` | 출처 네 곳과 **아는 것 / 모르는 것** 대조 |
| `/trust` | 수집 항목, 민감정보 처리, 정보주체 권리 |

## 디자인

방향은 **육아수첩 · 관공서 기록부**입니다. 이 분야에서 흔한 파스텔 베이비 톤도, 핀테크식
무채색도 쓰지 않았습니다. 정부가 나눠 주는 아기수첩 — 모눈 인쇄, 개월 수 탭, 도장, 표 — 을
기준으로 삼았습니다.

| 토큰 | 값 | 쓰임 |
|------|-----|------|
| `--paper` | `#F6F8F3` | 재생지에 가까운 차가운 오프화이트 |
| `--ink` | `#1C2620` | 도장 잉크에 가까운 진한 녹흑 |
| `--seal` | `#36AA1C` | 유효·현재 (앱과 같은 브랜드 그린) |
| `--past` | `#A8B2A5` | 이미 지나간 것 |
| `--stamp` | `#C2453D` | 마감·경고 (경보 빨강이 아니라 도장 빨강) |
| `--rule` | `#DCE3D6` | 모눈선 |

### 표제를 얇게 쓰는 이유

이 서비스의 목소리는 "다 받으세요"가 아니라 "이건 알고 이건 모릅니다"입니다.
700 굵기 고딕 헤드라인은 그 성격에 맞지 않아, Pretendard 300 을 크게 놓아 표제를 만듭니다.

한글 명조를 쓰면 더 좋았겠지만 `next/font` 에 한글 서브셋을 제공하는 구글 폰트가 없고,
런타임에 외부에서 받아 오면 방문자 IP 가 매번 나갑니다. 개인정보 고지를 따로 두는 서비스에서
그건 앞뒤가 맞지 않아 자체 호스팅한 Pretendard 만 씁니다. 숫자는 IBM Plex Mono 로 자릿수를
맞춥니다.

### 시그니처

홈의 **월령 타임라인**입니다. 문구로 설득하는 대신 축을 놓고 직접 옮겨 보게 합니다.
밴드 구성은 실제 정책 데이터 모델(대상 연령 상·하한, 지급액, 소급 개월)을 따랐고,
금액을 모르는 정책은 `미상` 으로 둡니다.

## 배포

`main` 에 들어가면 GitHub Actions 가 빌드해 GitHub Pages 로 올립니다. PR 에서는 배포하지 않고
빌드·타입·린트와 화면 검증만 돕니다.

배포가 끝나면 페이지 5개와 `sitemap.xml`·`robots.txt` 가 실제로 200 인지, 그리고 **CSS 가 붙는지**
확인합니다. 파일은 올라갔는데 `basePath` 가 어긋나 스타일만 404 인 경우가 흔해서입니다.

```bash
npm run verify # 로컬에서 같은 검사 (out/ 을 먼저 서빙해야 합니다)
```

### 수치 갱신

사이트에 적힌 수집량은 `src/content/figures.ts` 한 곳에 있고 `MEASURED_ON` 과 함께 둡니다.
백엔드가 매주 공공데이터를 새로 받아 오므로 값이 낡습니다.

1. `CareCode_Interface` 에서 `./gradlew liveSyncCheck` 로 실측
2. `figures.ts` 의 값과 `MEASURED_ON` 을 **함께** 수정
3. `main` 에 반영하면 자동 배포

값만 고치고 날짜를 안 고치면 거짓말이 됩니다. 매주 화요일 새벽에도 자동으로 다시 빌드하지만,
그건 코드에 적힌 값을 다시 그리는 것일 뿐 실측값을 새로 받아 오지는 않습니다.

## 원칙

**근거 없는 숫자는 쓰지 않습니다.** `/data` 에 무엇을 모르는지 적어 둔 것도 같은 이유입니다.
어린이집 자리와 지원금은 부모가 실제 결정을 내리는 정보라, 그럴듯한 추정치를 믿고 다른
선택지를 포기한 사람이 손해를 봅니다.

수치를 고칠 때는 [CareCode_Interface 문서](https://github.com/CareCode-Repo/CareCode_Interface/tree/main/docs)의
실측값을 근거로 삼으세요.

## 스택

Expand Down
10 changes: 10 additions & 0 deletions next.config.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
import type { NextConfig } from 'next'

/*
* GitHub Pages 프로젝트 페이지는 /<저장소명> 아래에 붙는다. basePath 를 주지 않으면
* CSS·JS 를 루트에서 찾아 전부 404 가 난다. 로컬 개발에서는 루트로 서비스하므로
* 값을 비워 두고, 배포 워크플로에서만 넣는다.
*
* 커스텀 도메인을 붙이면 이 변수를 비우면 된다.
*/
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ''

const nextConfig: NextConfig = {
/*
* 소개 사이트는 서버가 할 일이 없다. 정적 파일로 뽑아 어디든 올릴 수 있게 한다.
Expand All@@ -8,6 +17,7 @@ const nextConfig: NextConfig = {
output: 'export',
images: { unoptimized: true },
trailingSlash: true,
basePath,
}

export default nextConfig
Loading
Loading