Skip to content

Commit 81bfc0c

Browse files
박은우박은우
authored andcommitted
chore(repo): 원격 main 변경사항 병합
2 parents 8428223 + 7fbfae1 commit 81bfc0c

37 files changed

Lines changed: 263 additions & 1159 deletions

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ ADR 작성 기준:
4242
## 프로젝트 구조
4343

4444
- `app/`: Next.js App Router route adapter, route handler, metadata entry
45-
- `blog/`: 글 도메인. post schema, repository, publication policy, series,
46-
blog UI, RSS feed serialization, view-count use case
45+
- `blog/`: 글 도메인. post schema, repository, publication policy, blog UI,
46+
RSS feed serialization, view-count use case
4747
- `resume/`: 이력서 도메인. resume data, ordering, resume UI
4848
- `site/`: 도메인 조합 layer. home, AppShell, navigation, provider, site config
4949
- `infra/`: 외부/런타임 인프라. Supabase, Umami analytics, SEO helper,

app/engineering/series/[seriesId]/page.tsx

Lines changed: 0 additions & 11 deletions
This file was deleted.

app/sitemap.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ describe('sitemap', () => {
3838
);
3939
expect(urls).not.toContain(`${SITE_URL}/blog`);
4040
expect(urls).not.toContain(`${SITE_URL}/series`);
41+
expect(
42+
urls.some((url) => url.includes('/engineering/series/'))
43+
).toBe(false);
4144
});
4245

4346
it('uses updated date for post lastModified when available', () => {

app/sitemap.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { MetadataRoute } from 'next';
2-
import { getSeriesSummaries } from '@/blog/model/series-group';
32
import { getSortedFeedData } from '@/blog/services/post-repository';
43
import {
54
SITE_FEED_PATH,
@@ -17,15 +16,6 @@ export default function sitemap(): MetadataRoute.Sitemap {
1716
priority: 0.7,
1817
}));
1918

20-
const seriesEntries = getSeriesSummaries(
21-
feeds.filter((feed) => feed.category === 'Tech')
22-
).map((series) => ({
23-
url: createSiteUrl(`/engineering/series/${encodeURIComponent(series.id)}`),
24-
lastModified: series.latestDate,
25-
changeFrequency: 'weekly' as const,
26-
priority: 0.6,
27-
}));
28-
2919
const routePaths: SitePath[] = [
3020
'',
3121
'/archive',
@@ -43,5 +33,5 @@ export default function sitemap(): MetadataRoute.Sitemap {
4333
priority: 1,
4434
}));
4535

46-
return [...routes, ...seriesEntries, ...feedEntries];
36+
return [...routes, ...feedEntries];
4737
}

blog/model/series-group.test.ts

Lines changed: 0 additions & 92 deletions
This file was deleted.

blog/model/series-group.ts

Lines changed: 0 additions & 101 deletions
This file was deleted.

blog/services/policy.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { describe, expect, it } from 'vitest';
44
import type { FeedData } from '@/blog/model/types';
55
import { getAllFeedSlugs, getSortedFeedData } from './post-repository';
66
import {
7+
filterListedPosts,
78
filterVisiblePosts,
89
getCoreTechReviewAverage,
910
isEligibleForFeaturedPost,
11+
isListedPost,
1012
isPostVisible,
1113
meetsPublicTechReviewThreshold,
1214
PUBLICATION_POLICY,
@@ -64,6 +66,29 @@ describe('publication policy', () => {
6466
).toBe(false);
6567
});
6668

69+
it('excludes public tech posts that fail review thresholds from public listings', () => {
70+
const belowThresholdPost: FeedData = {
71+
title: 'Below threshold',
72+
slug: 'below-threshold',
73+
description: 'desc',
74+
date: '2026-01-01',
75+
category: 'Tech',
76+
contentType: 'essay',
77+
visibility: 'public',
78+
qualityReview: {
79+
philosophy: 2,
80+
design: 2,
81+
implementation: 2,
82+
},
83+
};
84+
85+
expect(isListedPost(belowThresholdPost)).toBe(false);
86+
expect(isListedPost(belowThresholdPost, { includePrivate: true })).toBe(
87+
true
88+
);
89+
expect(filterListedPosts([belowThresholdPost])).toEqual([]);
90+
});
91+
6792
it('keeps every private post out of public listings and static paths', () => {
6893
const allPosts = getSortedFeedData({ includePrivate: true });
6994
const privatePosts = allPosts.filter(

blog/services/policy.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,48 @@ export function isPostVisible(
1717
return options.includePrivate || post.visibility === 'public';
1818
}
1919

20+
export function isListedPost(
21+
post: FeedData,
22+
options: PublicationQueryOptions = {}
23+
): boolean {
24+
const visibility = post.visibility ?? 'private';
25+
26+
if (visibility !== 'public') {
27+
return options.includePrivate === true;
28+
}
29+
30+
if (options.includePrivate) {
31+
return true;
32+
}
33+
34+
if (
35+
post.category === 'Tech' &&
36+
!meetsPublicTechReviewThreshold(post.qualityReview)
37+
) {
38+
return false;
39+
}
40+
41+
if (post.featured && !isEligibleForFeaturedPost(post)) {
42+
return false;
43+
}
44+
45+
return true;
46+
}
47+
2048
export function filterVisiblePosts<T extends Pick<FeedData, 'visibility'>>(
2149
posts: T[],
2250
options: PublicationQueryOptions = {}
2351
): T[] {
2452
return posts.filter((post) => isPostVisible(post, options));
2553
}
2654

55+
export function filterListedPosts<T extends FeedData>(
56+
posts: T[],
57+
options: PublicationQueryOptions = {}
58+
): T[] {
59+
return posts.filter((post) => isListedPost(post, options));
60+
}
61+
2762
export function getCoreTechReviewAverage(
2863
review: QualityReview | undefined
2964
): number | null {

blog/services/post-repository.test.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
calculateReadingTime,
88
getFeedData,
99
getFolderSlug,
10-
getSeriesPosts,
1110
getSortedFeedData,
1211
getAllFeedSlugs,
1312
} from './post-repository';
@@ -60,19 +59,6 @@ ${'x'.repeat(5000)}
6059
expect(hasExtractedImage).toBe(true);
6160
});
6261

63-
it('does not retain learning series migrated to llm-wiki', () => {
64-
expect(getSeriesPosts('redis-deep-dive', { includePrivate: true })).toEqual(
65-
[]
66-
);
67-
expect(getSeriesPosts('flink-mastery', { includePrivate: true })).toEqual(
68-
[]
69-
);
70-
});
71-
72-
it('returns empty array for unknown series id', () => {
73-
expect(getSeriesPosts('non-existent-series-id')).toEqual([]);
74-
});
75-
7662
it('returns slug list with all feed entries', () => {
7763
const slugs = getAllFeedSlugs();
7864

0 commit comments

Comments
 (0)