Skip to content

Commit 74da87b

Browse files
SOIVclaude
andcommitted
docs: 공유 링크 코어 시스템 설계 추가
- technical/08-shared-link.md 신규 작성 - 모듈 공통 공개 링크 발행 인프라 설계 - DB 스키마 (shared_links, shared_link_logs) - API 명세 (발행/접근/무효화/목록) - 모듈 렌더 핸들러 등록 방식 - 보안 고려사항 (토큰 길이, bcrypt, 민감 데이터 필터링) - roadmap Phase 1.9에 1.9.4 공유 링크 코어 시스템 항목 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e16ee29 commit 74da87b

2 files changed

Lines changed: 166 additions & 0 deletions

File tree

docs/v2_FINANCIAL-LEDGER/roadmap/01-development-plan.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,11 +278,26 @@ Control 전체 목록과 상태 관리는 별도 문서에서 관리:
278278
- [ ] 임시 비밀번호 강제 변경 API (`POST /auth/password/change`)
279279
- [ ] 비밀번호 복구 흐름 API (관리자 토큰 발급 / SMTP 연동은 Phase 2.3으로)
280280

281+
#### 1.9.4 공유 링크 코어 시스템
282+
**예상 기간: 3일**
283+
284+
> 모든 모듈이 공통으로 사용할 수 있는 공개 링크 발행 인프라.
285+
> 청구서, 폼, 프로젝트 현황 등 어떤 데이터든 모듈이 이 코어를 호출하면 공개 링크를 발행할 수 있다.
286+
> 상세 설계는 `technical/08-shared-link.md` 참고.
287+
288+
- [ ] 공유 링크 DB 스키마 (`shared_links` 테이블 — 토큰, 대상 리소스, 만료일, 접근 제한 등)
289+
- [ ] 링크 발행 API (`POST /core/share`) — 모듈이 호출하는 공통 엔드포인트
290+
- [ ] 링크 조회 API (`GET /s/:token`) — 비인증 공개 접근, 토큰 유효성 검증
291+
- [ ] 만료/비밀번호/접근 횟수 제한 옵션 지원
292+
- [ ] 링크별 접근 로그 기록 (접속 시각, IP)
293+
- [ ] 링크 무효화 API (`DELETE /core/share/:token`)
294+
281295
### 마일스톤 1.9 완료 기준
282296
-`pnpm dev` 실행 시 API 서버가 실제로 기동되고 요청을 처리함
283297
- ✅ SQLite 기반 DB 연결 및 마이그레이션이 실제로 동작함
284298
- ✅ 로그인 → JWT 발급 → 인증 미들웨어 보호 라우트 접근이 실제로 동작함
285299
- ✅ 프론트엔드 Shell의 mock 인증이 실제 API 호출로 교체 가능한 상태
300+
- ✅ 공유 링크 발행 및 접근이 실제로 동작함
286301

287302
### 🔄 Phase 1.5 진행 이력
288303

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# 공유 링크 코어 시스템 (Shared Link)
2+
3+
## 1. 개요
4+
5+
모든 모듈이 공통으로 사용할 수 있는 **공개 링크 발행 인프라**입니다.
6+
모듈은 이 코어를 호출하기만 하면 어떤 데이터든 외부 공개 링크를 발행할 수 있습니다.
7+
8+
```
9+
[모듈] → POST /core/share → 토큰 발행
10+
[외부] → GET /s/:token → 토큰 검증 → 모듈 데이터 렌더링
11+
```
12+
13+
---
14+
15+
## 2. 모듈 연동 방식
16+
17+
각 모듈은 공유할 데이터의 **리소스 타입과 ID**만 코어에 전달합니다.
18+
링크 관리(발행, 만료, 접근 제어)는 전부 코어가 처리합니다.
19+
20+
```ts
21+
// 모듈에서 링크 발행 요청
22+
POST /core/share
23+
{
24+
resourceType: 'invoice', // 모듈이 정의한 리소스 타입
25+
resourceId: 'inv_abc123', // 해당 리소스 ID
26+
expiresAt: '2026-05-01', // 만료일 (optional)
27+
password: 'secret', // 비밀번호 보호 (optional)
28+
maxAccessCount: 10, // 최대 접근 횟수 (optional)
29+
}
30+
31+
// 응답
32+
{
33+
token: 'a1b2c3d4e5f6',
34+
url: 'https://your-server.com/s/a1b2c3d4e5f6'
35+
}
36+
```
37+
38+
---
39+
40+
## 3. DB 스키마
41+
42+
```sql
43+
CREATE TABLE shared_links (
44+
id TEXT PRIMARY KEY, -- UUID
45+
token TEXT UNIQUE NOT NULL, -- 공개 URL 토큰 (랜덤 12자 이상)
46+
resource_type TEXT NOT NULL, -- 모듈이 정의한 리소스 타입
47+
resource_id TEXT NOT NULL, -- 해당 리소스 ID
48+
password_hash TEXT, -- 비밀번호 해시 (없으면 NULL)
49+
max_access INTEGER, -- 최대 접근 횟수 (없으면 무제한)
50+
access_count INTEGER DEFAULT 0, -- 현재 접근 횟수
51+
expires_at DATETIME, -- 만료일시 (없으면 영구)
52+
created_by TEXT NOT NULL, -- 발행한 사용자 ID
53+
created_at DATETIME DEFAULT NOW(),
54+
revoked_at DATETIME -- 수동 무효화 시각 (NULL이면 유효)
55+
);
56+
57+
CREATE TABLE shared_link_logs (
58+
id TEXT PRIMARY KEY,
59+
token TEXT NOT NULL,
60+
accessed_at DATETIME DEFAULT NOW(),
61+
ip_address TEXT,
62+
user_agent TEXT
63+
);
64+
```
65+
66+
---
67+
68+
## 4. API 명세
69+
70+
### 링크 발행
71+
```
72+
POST /core/share
73+
Authorization: Bearer <jwt> ← 인증된 사용자만 발행 가능
74+
75+
Body: {
76+
resourceType: string
77+
resourceId: string
78+
expiresAt?: string // ISO 8601
79+
password?: string
80+
maxAccessCount?: number
81+
}
82+
83+
Response: { token: string, url: string }
84+
```
85+
86+
### 링크 접근 (공개, 비인증)
87+
```
88+
GET /s/:token
89+
90+
Query: ?password=secret ← 비밀번호 보호 링크의 경우
91+
92+
Response:
93+
200 → { resourceType, resourceId, data: <모듈이 제공하는 렌더 데이터> }
94+
401 → 비밀번호 필요 또는 불일치
95+
404 → 토큰 없음
96+
410 → 만료 또는 무효화됨
97+
429 → 접근 횟수 초과
98+
```
99+
100+
### 링크 무효화
101+
```
102+
DELETE /core/share/:token
103+
Authorization: Bearer <jwt> ← 발행자 또는 관리자만 가능
104+
```
105+
106+
### 내가 발행한 링크 목록
107+
```
108+
GET /core/share
109+
Authorization: Bearer <jwt>
110+
```
111+
112+
---
113+
114+
## 5. 모듈 측 구현 가이드
115+
116+
링크 접근 시 코어는 `resourceType``resourceId`를 해당 모듈에 위임합니다.
117+
각 모듈은 자신의 리소스 타입에 대한 **렌더 핸들러**를 등록해야 합니다.
118+
119+
```ts
120+
// 모듈에서 렌더 핸들러 등록 (module.json 또는 모듈 초기화 시)
121+
registerSharedLinkRenderer('invoice', async (resourceId, ctx) => {
122+
const invoice = await getInvoice(resourceId);
123+
return { ...invoice }; // 외부에 노출할 데이터만 반환
124+
});
125+
```
126+
127+
코어가 `/s/:token` 요청을 받으면:
128+
1. 토큰 유효성 검증 (만료, 비밀번호, 횟수)
129+
2. `resourceType`에 등록된 핸들러 호출
130+
3. 핸들러가 반환한 데이터를 응답
131+
132+
---
133+
134+
## 6. 보안 고려사항
135+
136+
- 토큰은 최소 **16자 이상의 랜덤 문자열** (crypto.randomBytes 기반)
137+
- 비밀번호는 평문 저장 금지 — bcrypt 해시로 저장
138+
- 접근 로그는 IP/User-Agent 기록 (텔레메트리 동의 여부와 무관하게 내부 로그로 유지)
139+
- 공개 링크 응답에는 **내부 ID, 사용자 정보 등 민감 데이터 포함 금지** — 모듈 핸들러가 필터링 책임
140+
- 무효화된 링크는 삭제하지 않고 `revoked_at`만 기록 (접근 시도 감사 추적 가능)
141+
142+
---
143+
144+
## 7. 활용 예시
145+
146+
| 모듈 | 리소스 타입 | 외부 접속자가 보는 것 |
147+
|------|------------|----------------------|
148+
| 청구서 | `invoice` | 청구서 내용, 결제 링크 |
149+
| 폼 빌더 | `form` | 응답 입력 폼 |
150+
| 프로젝트 | `project-status` | 진행 현황 (읽기 전용) |
151+
| 출퇴근 | `schedule` | 근무 일정 확인 |

0 commit comments

Comments
 (0)