Skip to content

[DEV] API Refactor#30

Open
yymin1022 wants to merge 12 commits into
mainfrom
dev/api-refactor
Open

[DEV] API Refactor#30
yymin1022 wants to merge 12 commits into
mainfrom
dev/api-refactor

Conversation

@yymin1022

Copy link
Copy Markdown
Owner

Summary

Blog_LR 내 자체 API 엔드포인트를 리팩토링 하였습니다.

Description

  • /getPostData, /getPostImage, /getPostList Next.js 루트 API 라우트가 Firebase 대신 원격 jsDelivr CDN 레포지토리와 연동하여 데이터를 가져오고 기존 백엔드 명세와 동일하게 호환되는 결과물(마크다운 Front Matter 보존 등)을 응답하도록 구현했습니다.
  • PostDataUtil.ts의 헬퍼 함수들이 직접 CDN을 바라보지 않고, Next.js 자체 API 엔드포인트를 전적으로 호출하도록 리팩토링했습니다.
  • Node.js 서버 사이드 렌더링 시에도 Next.js 내부 API를 정상적으로 호출할 수 있도록 환경 변수 기반의 getBaseUrl 절대경로 바인딩을 적용했습니다.
  • 자체 API가 반환한 Front Matter 포함 원문 마크다운 데이터를 PostDataUtil.ts에서 최종 stripFrontMatter 가공 후 컴포넌트에 반환하게 하여 화면 렌더링 호환성을 확보했습니다.
  • page.tsx, MDRender.tsx, PostCardDesktop.tsx에서 이미지 호출 대상을 새로운 자체 백엔드 엔드포인트 /getPostImage로 변경했습니다.

@yymin1022 yymin1022 requested a review from Copilot June 16, 2026 15:19
@yymin1022 yymin1022 self-assigned this Jun 16, 2026
@yymin1022 yymin1022 added the dev New feature or request label Jun 16, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the post and image fetching architecture by introducing internal API routes (/getPostList, /getPostData, /getPostImage) to handle CDN communication, updating frontend components to point to these new endpoints, and adding an INTERNAL_API_URL environment variable. Feedback focuses on addressing an architectural anti-pattern where Server Components make internal HTTP requests to their own API routes, which can degrade performance and reliability. Additionally, it is recommended to centralize the duplicated isSafeInput function, use absolute URLs for OpenGraph metadata images to prevent broken previews, and switch the post list retrieval endpoint from POST to GET to follow RESTful practices and enable caching.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/utils/PostDataUtil.ts
Comment on lines +34 to +37
const getBaseUrl = () => {
if (typeof window !== "undefined") return ""; // client-side 상대 경로 사용
return process.env.INTERNAL_API_URL || "http://localhost:3000"; // server-side 로컬 API 타겟
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

⚠️ 아키텍처 및 성능 우려: Server Component에서의 자체 API 호출 (안티패턴)

현재 리팩토링에서는 PostDataUtil.ts가 직접 CDN을 바라보지 않고, Next.js의 자체 API 엔드포인트(/getPostData, /getPostList 등)를 HTTP fetch를 통해 호출하도록 변경되었습니다.

하지만 Server Component(서버 사이드 렌더링)에서 동일한 Next.js 서버의 API 라우트를 HTTP로 호출하는 것은 Next.js 공식 문서에서도 권장하지 않는 안티패턴입니다.

발생할 수 있는 문제점:

  1. 성능 저하 (Latency): 동일한 서버 내부에서 불필요한 HTTP 요청/응답 오버헤드(TCP 핸드셰이크, 네트워크 홉, JSON 직렬화/역직렬화)가 추가되어 SSR/SSG 페이지 렌더링 속도가 느려집니다.
  2. 환경 변수 의존성 및 배포 취약성: 서버 사이드에서 로컬 API를 호출하기 위해 INTERNAL_API_URL과 같은 절대 경로 환경 변수가 필수적입니다. 배포 환경(Vercel, Docker 등)이나 포트 변경 시 오동작할 위험이 큽니다.
  3. 서버 리소스 고갈: 트래픽이 몰릴 경우, 서버가 자기 자신에게 HTTP 요청을 보내고 대기하는 과정에서 커넥션 풀 고갈이나 데드락(Deadlock)이 발생할 수 있습니다.

권장하는 해결 방안:

비즈니스 로직(CDN에서 데이터를 가져오고 가공하는 로직)을 공통 헬퍼 함수로 분리하고, Server Component와 API 라우트가 각각 이 함수를 직접 호출하도록 구조를 변경해야 합니다.

Comment on lines +4 to +10
function isSafeInput(input: string | null): boolean {
if (!input) return false;
if (input.includes("/") || input.includes("\\") || input.includes("..")) {
return false;
}
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

💡 코드 중복 제거: isSafeInput 공통화

isSafeInput 함수가 여러 API 라우트 파일(getPostData/route.ts, getPostImage/route.ts, getPostList/route.ts) 및 PostDataUtil.ts에 동일하게 중복 정의되어 있습니다.

코드 중복을 줄이고 유지보수성을 높이기 위해, 이 함수를 src/utils/PostDataUtil.ts에서만 정의하고 export하여 다른 API 라우트 파일들에서 import해서 사용하도록 리팩토링하는 것을 권장합니다.

Comment on lines 33 to 42
if (type === "solving") {
if (id.startsWith("boj")) {
imageUrl = "/api/getPostImage?postType=solving&postID=dummy&srcID=thumb_boj.png";
imageUrl = "/getPostImage?postType=solving&postID=dummy&srcID=thumb_boj.png";
} else if (id.startsWith("programmers")) {
imageUrl = "/api/getPostImage?postType=solving&postID=dummy&srcID=thumb_programmers.png";
imageUrl = "/getPostImage?postType=solving&postID=dummy&srcID=thumb_programmers.png";
}
} else {
const imageFolder = type === "about" ? id : PostURL;
imageUrl = `/api/getPostImage?postType=${type}&postID=${imageFolder}&srcID=post.png`;
imageUrl = `/getPostImage?postType=${type}&postID=${imageFolder}&srcID=post.png`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🌐 OpenGraph 이미지 절대 경로 사용 권장

OpenGraph 및 Twitter 카드용 이미지 URL(imageUrl)로 상대 경로(/getPostImage...)를 사용하고 있습니다.

Next.js에서는 메타데이터의 이미지 URL이 상대 경로일 경우, metadataBase가 설정되어 있지 않으면 프로덕션 환경에서 절대 경로로 올바르게 변환되지 않아 카카오톡, 디스코드, 트위터 등에서 미리보기 이미지가 깨질 수 있습니다.

안전한 메타데이터 제공을 위해 SITE_URL을 활용하여 절대 경로로 지정하는 것을 권장합니다.

Suggested change
if (type === "solving") {
if (id.startsWith("boj")) {
imageUrl = "/api/getPostImage?postType=solving&postID=dummy&srcID=thumb_boj.png";
imageUrl = "/getPostImage?postType=solving&postID=dummy&srcID=thumb_boj.png";
} else if (id.startsWith("programmers")) {
imageUrl = "/api/getPostImage?postType=solving&postID=dummy&srcID=thumb_programmers.png";
imageUrl = "/getPostImage?postType=solving&postID=dummy&srcID=thumb_programmers.png";
}
} else {
const imageFolder = type === "about" ? id : PostURL;
imageUrl = `/api/getPostImage?postType=${type}&postID=${imageFolder}&srcID=post.png`;
imageUrl = `/getPostImage?postType=${type}&postID=${imageFolder}&srcID=post.png`;
}
if (type === "solving") {
if (id.startsWith("boj")) {
imageUrl = `${SITE_URL}/getPostImage?postType=solving&postID=dummy&srcID=thumb_boj.png`;
} else if (id.startsWith("programmers")) {
imageUrl = `${SITE_URL}/getPostImage?postType=solving&postID=dummy&srcID=thumb_programmers.png`;
}
} else {
const imageFolder = type === "about" ? id : PostURL;
imageUrl = `${SITE_URL}/getPostImage?postType=${type}&postID=${imageFolder}&srcID=post.png`;
}

Comment on lines +12 to +15
export async function POST(req: NextRequest) {
try {
const body = await req.json();
const { postType } = body;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

🌐 RESTful API 디자인: 조회(Read) 작업에 GET 메서드 사용 권장

현재 포스트 목록을 가져오는 API가 POST 메서드로 구현되어 있습니다.

단순히 데이터를 조회(Read)하는 작업은 HTTP GET 메서드를 사용하고, 필요한 파라미터(postType)는 쿼리 스트링(Query String)으로 전달하는 것이 RESTful API 설계 원칙에 부합합니다.

GET 메서드를 사용할 경우 다음과 같은 장점이 있습니다:

  1. 브라우저 및 CDN 캐싱 활용 가능: POST 요청은 기본적으로 캐싱되지 않지만, GET 요청은 브라우저나 CDN 레벨에서 손쉽게 캐싱할 수 있어 성능을 크게 향상시킬 수 있습니다.
  2. 디버깅 용이성: 브라우저 주소창에 직접 URL을 입력하여 API 응답을 즉시 확인할 수 있습니다.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

자체 API 엔드포인트(/getPostData, /getPostList, /getPostImage)를 신설/정리하여, 게시글/목록/이미지 데이터를 Firebase 대신 jsDelivr CDN 기반으로 가져오고(Front Matter 포함 원문 마크다운 유지), 프론트 유틸(PostDataUtil.ts)은 CDN 직접 접근 대신 내부 API 호출로 통일한 리팩토링입니다.

Changes:

  • /getPostData, /getPostList 라우트 핸들러를 추가해 CDN의 posts.jsonpost.md를 백엔드 명세 형태로 래핑 응답
  • PostDataUtil.ts의 데이터/이미지 접근을 내부 API 호출로 전환하고, 서버 컴포넌트용 절대 URL 바인딩(getBaseUrl) 추가
  • 이미지 요청 경로를 /api/getPostImage/getPostImage로 변경하고, 레거시 /api/getPostData 라우트 제거

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/utils/PostDataUtil.ts CDN 직접 fetch 제거 후 내부 API 호출로 통일, SSR용 base URL 처리 및 Front Matter strip 적용
src/app/getPostList/route.ts CDN posts.json 기반 목록 API 신규 추가
src/app/getPostData/route.ts CDN posts.json + post.md 기반 단일 포스트 API 신규 추가
src/app/getPostImage/route.ts 이미지 fetch를 CDN 직접 호출 방식으로 정리(POST: base64 JSON, GET: 바이너리 응답 유지)
src/app/api/getPostData/route.ts 레거시 /api/getPostData 라우트 제거
src/app/[type]/[id]/page.tsx OG 이미지 URL을 /getPostImage로 변경
src/app/[type]/_component/PostCard/PostCardDesktop.tsx 카드 썸네일 URL을 /getPostImage로 변경
src/app/[type]/_component/MDRender/MDRender.tsx 마크다운 이미지 URL을 /getPostImage로 변경
.env.example SSR 내부 API 호출용 INTERNAL_API_URL 예시 추가 및 설명 정리

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/PostDataUtil.ts

export const CDN_BASE_URL = "https://cdn.jsdelivr.net/gh/yymin1022/Blog_LR_Data@master";
export const SITE_URL = process.env.URL_PUB || "https://dev-lr.com";
export const SITE_URL = process.env.URL_PUB || "http://localhost:3000";
Comment thread src/utils/PostDataUtil.ts
Comment on lines +34 to +37
const getBaseUrl = () => {
if (typeof window !== "undefined") return ""; // client-side 상대 경로 사용
return process.env.INTERNAL_API_URL || "http://localhost:3000"; // server-side 로컬 API 타겟
};
Comment on lines +37 to +38
const postsIndex = await indexResponse.json() as Record<string, PostData[]>;
const categoryPosts = postsIndex && Array.isArray(postsIndex[postType]) ? postsIndex[postType] : [];
Comment on lines +37 to +39
const postsIndex = await indexResponse.json() as Record<string, PostData[]>;
const categoryPosts = postsIndex && Array.isArray(postsIndex[postType]) ? postsIndex[postType] : [];
const post = categoryPosts.find((p) => p.postID === postID);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants