From afe196cc7add377f0a1cc14a9fdc7384096fb7ae Mon Sep 17 00:00:00 2001 From: minsoo-web Date: Sat, 28 Mar 2026 11:58:31 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20hamsurang=20org=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=2018=EB=AA=85=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=EC=8B=AD=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1기 멤버 + org 멤버 18명을 People 섹션에 추가 (11명 → 29명) - 기존 멤버 닉네임을 실명으로 변경 - middleware 검증을 PEOPLE_MBTI_INFO_MAP에서 HAMSURANG_PEOPLE로 변경하여 멤버십과 MBTI 데이터 분리 - PROFILE_INFO satisfies 제약을 HAMSURANG_PEOPLE 기준으로 변경 - MBTI 미등록 멤버를 위한 null guard + 플레이스홀더 추가 - MDX README 미작성 멤버를 위한 에러 핸들링 + 폴백 UI 추가 - React key를 name에서 username으로 변경 (고유성 보장) Constraint: MBTI 데이터와 MDX README는 본인 입력 필요 — graceful degradation으로 처리 Rejected: MBTI placeholder 데이터 삽입 | 허위 성격 데이터가 실제 데이터처럼 표시됨 Confidence: high Scope-risk: moderate Not-tested: iframe 임베딩 환경(web 앱)에서의 신규 멤버 페이지 동작 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../@profile/people/people.constants.ts | 78 ++++++++++++++- .../(profile-readme)/@readme/people/page.tsx | 8 +- .../home/app/(profile-readme)/people/page.tsx | 7 +- .../components/People/People.constants.ts | 96 ++++++++++++++++--- .../app/_shared/components/People/People.tsx | 2 +- apps/home/middleware.ts | 6 +- 6 files changed, 179 insertions(+), 18 deletions(-) diff --git a/apps/home/app/(profile-readme)/@profile/people/people.constants.ts b/apps/home/app/(profile-readme)/@profile/people/people.constants.ts index 8691a7f..362a38d 100644 --- a/apps/home/app/(profile-readme)/@profile/people/people.constants.ts +++ b/apps/home/app/(profile-readme)/@profile/people/people.constants.ts @@ -1,6 +1,8 @@ -import type { PEOPLE_MBTI_INFO_MAP } from '@/(profile-readme)/people/people.constants' +import type { HAMSURANG_PEOPLE } from '@/_shared/components/People/People.constants' import type { ProfileProps } from '@/_shared/components/Profile/Profile' +type PeopleUsername = (typeof HAMSURANG_PEOPLE)[number]['username'] + export const PROFILE_INFO = { 'minsoo-web': { name: 'Minsoo kim', @@ -54,6 +56,78 @@ export const PROFILE_INFO = { username: 'yejineee', email: 'yygeniee@gmail.com', }, + 'azure-553': { + name: '심미진', + username: 'azure-553', + }, + Sangminnn: { + name: '박상민', + username: 'Sangminnn', + }, + 'jong-kyung': { + name: '이종경', + username: 'jong-kyung', + }, + sHyunis: { + name: '정소현', + username: 'sHyunis', + }, + 'Seung-wan': { + name: '유승완', + username: 'Seung-wan', + }, + oilater: { + name: '김성현', + username: 'oilater', + }, + 'JeongHwan-dev': { + name: '박정환', + username: 'JeongHwan-dev', + }, + tolluset: { + name: '이병현', + username: 'tolluset', + }, + SeolJaeHyeok: { + name: '설재혁', + username: 'SeolJaeHyeok', + }, + jangwonyoon: { + name: '윤장원', + username: 'jangwonyoon', + }, + Kyujenius: { + name: '홍규진', + username: 'Kyujenius', + }, + Jxxunnn: { + name: '이준근', + username: 'Jxxunnn', + }, + doyoonear: { + name: '이도윤', + username: 'doyoonear', + }, + 'BO-LIKE-CHICKEN': { + name: '백보성', + username: 'BO-LIKE-CHICKEN', + }, + sudosubin: { + name: '김수빈', + username: 'sudosubin', + }, + jaesoekjjang: { + name: '이재석', + username: 'jaesoekjjang', + }, + kangju2000: { + name: '강주혁', + username: 'kangju2000', + }, + 'korkt-kim': { + name: '김경태', + username: 'korkt-kim', + }, } satisfies { - [key in keyof typeof PEOPLE_MBTI_INFO_MAP]: ProfileProps + [key in PeopleUsername]: ProfileProps } diff --git a/apps/home/app/(profile-readme)/@readme/people/page.tsx b/apps/home/app/(profile-readme)/@readme/people/page.tsx index 9b7cc2e..6ab6802 100644 --- a/apps/home/app/(profile-readme)/@readme/people/page.tsx +++ b/apps/home/app/(profile-readme)/@readme/people/page.tsx @@ -12,7 +12,13 @@ export default async function Page({ process.cwd(), `./app/(profile-readme)/@readme/_shared/content/${username}.mdx`, ) - const readme = await fs.readFile(filePath, { encoding: 'utf8' }) + + let readme: string + try { + readme = await fs.readFile(filePath, { encoding: 'utf8' }) + } catch { + return

아직 README가 작성되지 않았습니다.

+ } return } diff --git a/apps/home/app/(profile-readme)/people/page.tsx b/apps/home/app/(profile-readme)/people/page.tsx index 64cef97..30239b0 100644 --- a/apps/home/app/(profile-readme)/people/page.tsx +++ b/apps/home/app/(profile-readme)/people/page.tsx @@ -7,10 +7,15 @@ export default function Page({ searchParams: { [key: string]: string | string[] | undefined } }) { const username = searchParams.username + const mbtiData = PEOPLE_MBTI_INFO_MAP[username as keyof typeof PEOPLE_MBTI_INFO_MAP] return (
- + {mbtiData ? ( + + ) : ( +

아직 MBTI가 등록되지 않았습니다.

+ )}
) } diff --git a/apps/home/app/_shared/components/People/People.constants.ts b/apps/home/app/_shared/components/People/People.constants.ts index 20c5ea3..98f9ffe 100644 --- a/apps/home/app/_shared/components/People/People.constants.ts +++ b/apps/home/app/_shared/components/People/People.constants.ts @@ -1,46 +1,120 @@ export const HAMSURANG_PEOPLE = [ + // 0기 { - name: '쿼카', + name: '김민수', username: 'minsoo-web', }, { - name: '쏘니', + name: '손수림', username: 'sonsurim', }, { - name: '마누', + name: '고종현', username: 'manudeli', }, { - name: '퉁이리', + name: '정충일', username: 'tooooo1', }, { - name: '쉽', + name: '양예진', username: 'yejineee', }, { - name: 'P민수', + name: '박민수', username: 'minsour', }, { - name: '웨일', + name: '이현재', username: '2-NOW', }, { - name: '모리', + name: '김채림', username: 'chaaerim', }, { - name: '민케이', + name: '강민수', username: 'minchodang', }, { - name: '삼바', + name: '임성호', username: 'sungh0lim', }, { - name: '오키나와', + name: '박찬혁', username: 'okinawaa', }, + // 1기 + { + name: '심미진', + username: 'azure-553', + }, + { + name: '박상민', + username: 'Sangminnn', + }, + { + name: '이종경', + username: 'jong-kyung', + }, + { + name: '정소현', + username: 'sHyunis', + }, + { + name: '유승완', + username: 'Seung-wan', + }, + { + name: '김성현', + username: 'oilater', + }, + { + name: '박정환', + username: 'JeongHwan-dev', + }, + { + name: '이병현', + username: 'tolluset', + }, + { + name: '설재혁', + username: 'SeolJaeHyeok', + }, + { + name: '윤장원', + username: 'jangwonyoon', + }, + { + name: '홍규진', + username: 'Kyujenius', + }, + { + name: '이준근', + username: 'Jxxunnn', + }, + { + name: '이도윤', + username: 'doyoonear', + }, + { + name: '백보성', + username: 'BO-LIKE-CHICKEN', + }, + { + name: '김수빈', + username: 'sudosubin', + }, + { + name: '이재석', + username: 'jaesoekjjang', + }, + { + name: '강주혁', + username: 'kangju2000', + }, + { + name: '김경태', + username: 'korkt-kim', + }, ] as const diff --git a/apps/home/app/_shared/components/People/People.tsx b/apps/home/app/_shared/components/People/People.tsx index 8568678..bdd19be 100644 --- a/apps/home/app/_shared/components/People/People.tsx +++ b/apps/home/app/_shared/components/People/People.tsx @@ -37,7 +37,7 @@ export const People = () => {
{HAMSURANG_PEOPLE.map(({ name, username }) => ( - + p.username) + export function middleware(request: NextRequest) { const { searchParams, origin } = request.nextUrl const username = searchParams.get('username') - if (!username || !PEOPLE_MBTI_INFO_MAP[username as keyof typeof PEOPLE_MBTI_INFO_MAP]) { + if (!username || !validUsernames.includes(username)) { return NextResponse.redirect(new URL('/', origin)) } From 8c879d138b565352e6103e632ce1e5132416bae7 Mon Sep 17 00:00:00 2001 From: minsoo-web Date: Sat, 28 Mar 2026 22:16:17 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20next-mdx-remote=205.0.0=20=E2=86=92?= =?UTF-8?q?=206.0.0=20=EB=B3=B4=EC=95=88=20=EC=B7=A8=EC=95=BD=EC=A0=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vercel 배포에서 CVE-2026-0969로 차단되던 문제 해결. v6의 breaking change(blockJS 기본값 true)는 프로젝트에서 JS 표현식을 사용하지 않으므로 영향 없음. Constraint: Vercel이 next-mdx-remote 5.0.0 배포를 차단 Confidence: high Scope-risk: narrow Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/home/package.json | 2 +- apps/wiki/package.json | 2 +- pnpm-lock.yaml | 53 +++++++++++++++++++----------------------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/apps/home/package.json b/apps/home/package.json index bb464fb..6b79dff 100644 --- a/apps/home/package.json +++ b/apps/home/package.json @@ -20,7 +20,7 @@ "dayjs": "^1.11.11", "framer-motion": "^11.2.4", "next": "^14.2.11", - "next-mdx-remote": "^5.0.0", + "next-mdx-remote": "^6.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-use": "^17.5.0" diff --git a/apps/wiki/package.json b/apps/wiki/package.json index e2d7dc8..70432e0 100644 --- a/apps/wiki/package.json +++ b/apps/wiki/package.json @@ -16,7 +16,7 @@ "@mdx-js/react": "^3.0.1", "@next/mdx": "^14.2.5", "next": "^14.2.11", - "next-mdx-remote": "^5.0.0", + "next-mdx-remote": "^6.0.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8b1fe2..98dce46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: specifier: ^14.2.11 version: 14.2.11(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next-mdx-remote: - specifier: ^5.0.0 - version: 5.0.0(@types/react@18.2.61)(react@18.2.0) + specifier: ^6.0.0 + version: 6.0.0(@types/react@18.2.61)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -121,8 +121,8 @@ importers: specifier: ^14.2.11 version: 14.2.11(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next-mdx-remote: - specifier: ^5.0.0 - version: 5.0.0(@types/react@18.2.61)(react@18.2.0) + specifier: ^6.0.0 + version: 6.0.0(@types/react@18.2.61)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -2448,8 +2448,8 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next-mdx-remote@5.0.0: - resolution: {integrity: sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ==} + next-mdx-remote@6.0.0: + resolution: {integrity: sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ==} engines: {node: '>=14', npm: '>=7'} peerDependencies: react: '>=16' @@ -2977,9 +2977,6 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} - unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} @@ -2992,21 +2989,21 @@ packages: unist-util-remove-position@5.0.0: resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - unist-util-remove@3.1.1: - resolution: {integrity: sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==} + unist-util-remove@4.0.0: + resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==} unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - unist-util-visit-parents@5.1.3: - resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} - unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + update-browserslist-db@1.0.15: resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true @@ -5526,13 +5523,14 @@ snapshots: neo-async@2.6.2: {} - next-mdx-remote@5.0.0(@types/react@18.2.61)(react@18.2.0): + next-mdx-remote@6.0.0(@types/react@18.2.61)(react@18.2.0): dependencies: '@babel/code-frame': 7.24.2 '@mdx-js/mdx': 3.0.1 '@mdx-js/react': 3.0.1(@types/react@18.2.61)(react@18.2.0) react: 18.2.0 - unist-util-remove: 3.1.1 + unist-util-remove: 4.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.2 vfile-matter: 5.0.0 transitivePeerDependencies: @@ -6094,10 +6092,6 @@ snapshots: trough: 2.2.0 vfile: 6.0.2 - unist-util-is@5.2.1: - dependencies: - '@types/unist': 2.0.10 - unist-util-is@6.0.0: dependencies: '@types/unist': 3.0.2 @@ -6115,21 +6109,16 @@ snapshots: '@types/unist': 3.0.2 unist-util-visit: 5.0.0 - unist-util-remove@3.1.1: + unist-util-remove@4.0.0: dependencies: - '@types/unist': 2.0.10 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.2 - unist-util-visit-parents@5.1.3: - dependencies: - '@types/unist': 2.0.10 - unist-util-is: 5.2.1 - unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.2 @@ -6141,6 +6130,12 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + update-browserslist-db@1.0.15(browserslist@4.23.0): dependencies: browserslist: 4.23.0