Skip to content

Commit c04d9b5

Browse files
authored
refactor(site): 홈과 Resume 공개 경험 정리 (#116)
* feat(content): 에세이 공개 전환과 이력서 그리드 갱신 (#114) * feat(content): publish revised essays and resume * fix(site): address PR review feedback --------- * docs(readme): fix formatting and remove Three.js Updated the README to correct link formatting and removed the Three.js section. * refactor(site): 탐색과 읽기 경험 단순화 (#115) * feat(content): 에세이와 이력서 공개 개선 * fix(site): PR 리뷰 피드백 반영 * refactor(blog): 비공개 WebGL 시각화 제거 * docs(content): Three.js 구현 서사 정리 * refactor(site): 전역 내비게이션과 조작 요소 단순화 * refactor(home): 아카이브 탐색 흐름 단순화 * style(site): Graphite Ink 편집 표면 적용 * docs(adr): 사이트 단순화 결정 기록 * refactor(site): 모바일 읽기 흐름 개선 모바일 헤더와 메뉴를 텍스트 중심으로 단순화하고 홈 아카이브에서 날짜와 제목의 읽기 순서를 조정했습니다.\n\nADR 0035에 모바일 아카이브와 메뉴의 반복 규칙을 기록했습니다. * refactor(blog): 콘텐츠 공개 기준 정리 공개 후보 글을 현재 구조와 검증 기준에 맞춰 리라이팅했습니다. Redis·Flink 학습 시리즈는 llm-wiki raw·draft로 이관하고 Ark에서 제거했습니다. --------- * refactor(site): 홈과 Archive 중심 구조 정리 홈을 철학 문장과 핵심 경로로 축소하고, 게시글 탐색은 별도 Archive로 분리했습니다. 2-6-4 shell, 모바일 탐색, 글 목록 행, 기존 /blog redirect를 함께 정리했습니다. * refactor(resume): 공개 이력을 요약 그리드로 재구성 회사와 팀 맥락, 프로젝트의 간략 소개·상세 설명·직접 회고 링크만 남겼습니다. 개인 CTR 파이프라인과 데이터 플랫폼 역량을 함께 보여주도록 정리했습니다. * refactor(ui): 경로 오류를 텍스트 상태로 단순화 공통 EmptyState 대신 콘텐츠 열에 맞는 상태 표식과 텍스트 재시도 행동을 사용합니다. * docs(adr): 홈과 Resume 정보 구조 결정 기록 홈·Archive shell, Resume 요약과 회고 링크, 경로 오류 상태의 선택 기준을 ADR로 남기고 인덱스를 갱신했습니다. * test(e2e): stabilize archive spacing check
1 parent 7802428 commit c04d9b5

173 files changed

Lines changed: 3256 additions & 11904 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
name: blog-title-review
3+
description: Review, generate, and choose Ark blog post titles. Use when evaluating title candidates for posts/**/index.mdx and sibling meta.json, when the user is stuck choosing a title, or when a draft needs a click-worthy but accurate title that fits the first three paragraphs.
4+
---
5+
6+
# Blog Title Review
7+
8+
## Workflow
9+
10+
1. Read `docs/blog-quality-guide.md`, the target `index.mdx`, and the sibling
11+
`meta.json` before judging a repository post. If the post is not in the
12+
workspace, use the text the user provided.
13+
2. Preserve `category`, `contentType`, `slug`, and publication state unless the
14+
user explicitly asks for edits.
15+
3. Extract a title brief before proposing titles:
16+
- target reader and reading situation
17+
- the post's concrete object, event, or tension
18+
- the emotional stake or practical promise
19+
- the strongest first-three-paragraph hook
20+
- the current title and why it works or fails
21+
4. Score existing user candidates first when they exist, then add better
22+
alternatives only when useful.
23+
5. Evaluate the title and opening as one unit. If a candidate depends on a hook
24+
that the first three paragraphs do not pay off, flag the required opening
25+
change instead of pretending the title is ready.
26+
6. Do not edit files unless the user explicitly asks for implementation.
27+
28+
## Title Principles
29+
30+
- Prefer a specific unanswered question over a complete summary.
31+
- Use concrete nouns from the post before abstract virtues.
32+
- Create tension through contrast, self-recognition, reversal, or specificity.
33+
- Let the title be click-worthy, but keep the promise payable by the intro.
34+
- Check word texture. Reject words whose military, corporate, clinical, or
35+
overly visible metaphorical register clashes with the essay's emotional tone.
36+
- Avoid titles that sound like generic self-help, corporate slogans, product
37+
reviews, or manipulative bait.
38+
- Avoid over-weighting SEO when the post is an essay; the title still needs a
39+
human reason to click.
40+
41+
## Candidate Patterns
42+
43+
Generate candidates across multiple patterns when the user needs options:
44+
45+
- Collision: combine two unlike elements that the post genuinely connects.
46+
- Confession: expose the private pressure, mistake, or need behind the post.
47+
- Reversal: turn an expected belief into the post's real conclusion.
48+
- Object-led: let a concrete object carry the emotional promise.
49+
- Direct problem: name the reader's problem in plain language.
50+
- Quiet essay: use a restrained line when click pressure would cheapen the tone.
51+
52+
## Scorecard
53+
54+
Use 1-5 scores in 0.5 increments.
55+
56+
- `click`: likelihood that the title earns a click from the intended reader.
57+
- `fit`: accuracy to the full post, not only one vivid sentence.
58+
- `introFit`: whether the first three paragraphs pay off the title.
59+
- `specificity`: concrete nouns, situations, or stakes.
60+
- `emotion`: felt tension without melodrama.
61+
- `novelty`: freshness compared with generic essay titles.
62+
- `texture`: whether the key words feel natural in the post's emotional register.
63+
- `arkTone`: fit with Ark's direct, reflective, engineering-adjacent voice.
64+
- `baitRisk`: risk of overpromising or misframing the post; lower is better.
65+
66+
When ranking titles, prioritize high `click`, `fit`, and `introFit`, then use
67+
`baitRisk` as a veto. A high-click title with high bait risk should be marked as
68+
usable only after an intro rewrite.
69+
70+
## Output Format
71+
72+
Return:
73+
74+
```markdown
75+
## Title Brief
76+
- reader:
77+
- promise:
78+
- tension:
79+
- current title:
80+
81+
## Recommendation
82+
1. `...` - ...
83+
2. `...` - ...
84+
3. `...` - ...
85+
86+
## Scorecard
87+
| Title | Click | Fit | Intro Fit | Specificity | Emotion | Novelty | Texture | Ark Tone | Bait Risk | Note |
88+
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- |
89+
90+
## Opening Adjustment
91+
...
92+
```
93+
94+
Keep the output shorter when the user asks for a quick choice. Omit
95+
`Opening Adjustment` when every recommended title already matches the opening.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface:
2+
display_name: "Blog Title Review"
3+
short_description: "블로그 제목 후보를 점수화해 더 쉽게 선택합니다"
4+
default_prompt: "Use $blog-title-review to evaluate title candidates for a draft blog post."

AGENTS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ ADR 작성 기준:
4545
- `blog/`: 글 도메인. post schema, repository, publication policy, series,
4646
blog UI, RSS feed serialization, view-count use case
4747
- `resume/`: 이력서 도메인. resume data, ordering, resume UI
48-
- `search/`: 검색 도메인. command palette, search action, recommendation
4948
- `site/`: 도메인 조합 layer. home, AppShell, navigation, provider, site config
5049
- `infra/`: 외부/런타임 인프라. Supabase, Umami analytics, SEO helper,
5150
integration adapter

README.md

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@
77

88
아크는 오래 건너갈 생각들을 싣는 개인의 방주입니다.
99

10-
[라이브 데모](https://ark-log.vercel.app)
10+
[https://ark-log.vercel.app](https://ark-log.vercel.app)
1111

1212
</div>
1313

14-
운영 URL은 `NEXT_PUBLIC_SITE_URL`로 주입할 수 있습니다. 기본 운영 도메인은
15-
`https://ark-log.vercel.app`입니다.
16-
1714
## 🛠 기술 스택
1815

1916
<table>
@@ -31,10 +28,6 @@
3128
<br>TypeScript
3229
</td>
3330
<td align="center" width="96">
34-
<img src="https://skillicons.dev/icons?i=threejs" width="48" height="48" alt="Three.js" />
35-
<br>Three.js
36-
</td>
37-
<td align="center" width="96">
3831
<img src="https://skillicons.dev/icons?i=tailwind" width="48" height="48" alt="CSS" />
3932
<br>Tailwind
4033
</td>
@@ -49,7 +42,6 @@
4942

5043
**애니메이션:**
5144

52-
- **3D:** Three.js + @react-three/fiber + @react-three/drei
5345
- **모션:** Framer Motion
5446

5547
**콘텐츠 처리:**
@@ -80,7 +72,6 @@ ark/
8072
├── 📁 app/ # Next.js route adapter
8173
├── 📁 blog/ # 글 도메인
8274
├── 📁 resume/ # 이력서 도메인
83-
├── 📁 search/ # 검색 도메인
8475
├── 📁 site/ # 홈, AppShell, provider, site config
8576
├── 📁 infra/ # Supabase, Umami, SEO integration
8677
├── 📁 ui/ # 도메인 지식 없는 UI/레이아웃/모션

app/api/og/route.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export async function GET(req: NextRequest) {
4040
flexDirection: 'column',
4141
alignItems: 'flex-start',
4242
justifyContent: 'center',
43-
backgroundImage: 'linear-gradient(to bottom right, #E0F2FF, #fff)',
43+
backgroundImage: 'linear-gradient(to bottom right, #EAEBEA, #E0E1E3)',
4444
padding: '80px',
4545
}}
4646
>
@@ -56,7 +56,7 @@ export async function GET(req: NextRequest) {
5656
<div
5757
style={{
5858
fontSize: '30px',
59-
color: '#6b7684',
59+
color: '#52525B',
6060
}}
6161
>
6262
{date}
@@ -66,7 +66,7 @@ export async function GET(req: NextRequest) {
6666
style={{
6767
fontSize: '60px',
6868
fontWeight: 700,
69-
color: '#191f28',
69+
color: '#252525',
7070
lineHeight: 1.2,
7171
wordBreak: 'keep-all',
7272
}}
@@ -86,8 +86,8 @@ export async function GET(req: NextRequest) {
8686
key={tag}
8787
style={{
8888
display: 'flex',
89-
backgroundColor: 'rgba(49, 130, 246, 0.1)',
90-
color: '#3182f6',
89+
backgroundColor: '#E0E1E3',
90+
color: '#3F3F46',
9191
padding: '8px 24px',
9292
borderRadius: '50px',
9393
fontSize: '24px',
@@ -114,7 +114,7 @@ export async function GET(req: NextRequest) {
114114
style={{
115115
fontSize: '32px',
116116
fontWeight: 700,
117-
color: '#3182f6',
117+
color: '#3F3F46',
118118
fontFamily: 'Pretendard',
119119
}}
120120
>

app/archive/page.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Metadata } from 'next';
2+
import ArchivePage from '@/site/archive/ui/pages/ArchivePage';
3+
import { createSiteUrl } from '@/site/config/site';
4+
5+
const archiveUrl = createSiteUrl('/archive');
6+
7+
export const metadata: Metadata = {
8+
title: 'Archive',
9+
description: 'Ark에 쌓인 기술과 삶의 기록',
10+
alternates: {
11+
canonical: archiveUrl,
12+
},
13+
openGraph: {
14+
title: 'Archive',
15+
description: 'Ark에 쌓인 기술과 삶의 기록',
16+
url: archiveUrl,
17+
},
18+
};
19+
20+
export default ArchivePage;

app/blog/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
export { metadata, default } from '@/blog/ui/pages/BlogListPage';
1+
import { redirect } from 'next/navigation';
2+
3+
export default function BlogIndexPage() {
4+
redirect('/archive');
5+
}

app/layout.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ describe('root layout font loading', () => {
1313
expect(layoutContent).not.toContain('href="/fonts/TossFaceFontWeb.otf"');
1414
});
1515

16-
it('passes a public client DTO instead of full editorial metadata', () => {
17-
expect(layoutContent).toContain('selectClientPosts(getSortedFeedData())');
18-
expect(layoutContent).toContain('<AppProviders posts={posts}>');
16+
it('keeps the app shell independent from the archive feed', () => {
17+
expect(layoutContent).not.toContain('getSortedFeedData');
18+
expect(layoutContent).toContain('<AppProviders>');
19+
expect(layoutContent).toContain('<AppShell>{children}</AppShell>');
1920
});
2021

2122
it('keeps Tossface demand-driven for matching emoji glyphs', () => {

app/layout.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import '@/styles/globals.css';
44
import '@/styles/tossface.css';
55

66
import AppProviders from '@/site/providers/AppProviders';
7-
import { getSortedFeedData } from '@/blog/services/post-repository';
8-
import { selectClientPosts } from '@/site/providers/client-posts';
97
import { AppShell } from '@/site/shell/AppShell';
108
import {
119
SITE_AUTHOR,
@@ -55,16 +53,14 @@ export const metadata: Metadata = {
5553
};
5654

5755
export const viewport: Viewport = {
58-
themeColor: '#ffffff',
56+
themeColor: '#EAEBEA',
5957
width: 'device-width',
6058
initialScale: 1,
6159
};
6260

6361
export default function RootLayout({ children }: { children: ReactNode }) {
64-
const posts = selectClientPosts(getSortedFeedData());
65-
6662
return (
67-
<html lang="ko" suppressHydrationWarning>
63+
<html lang="ko">
6864
<head>
6965
<link
7066
rel="preload"
@@ -75,11 +71,10 @@ export default function RootLayout({ children }: { children: ReactNode }) {
7571
/>
7672
</head>
7773
<body>
78-
<AppProviders posts={posts}>
74+
<AppProviders>
7975
<div id="app-root">
80-
<AppShell posts={posts}>{children}</AppShell>
76+
<AppShell>{children}</AppShell>
8177
</div>
82-
<div id="overlay-root" />
8378
</AppProviders>
8479
</body>
8580
</html>

app/not-found.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function NotFound() {
1717
</p>
1818
<Link
1919
href="/"
20-
className="mt-8 inline-flex min-h-11 items-center justify-center rounded-[var(--radius-action)] bg-[var(--color-toss-blue)] px-5 text-sm font-semibold text-white transition-colors hover:bg-[var(--color-toss-blue-dark)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-toss-blue)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-bg-primary)]"
20+
className="mt-8 inline-flex min-h-11 items-center justify-center rounded-[var(--radius-action)] bg-[var(--color-toss-blue)] px-5 text-sm font-semibold text-[var(--color-accent-foreground)] transition-colors hover:bg-[var(--color-toss-blue-dark)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-toss-blue)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-bg-primary)]"
2121
>
2222
홈으로 돌아가기
2323
</Link>

0 commit comments

Comments
 (0)