Skip to content

[Feature/article] 아티클 API 연동 구조 정리 및 다국어 자동 번역 적용#203

Open
Yunil-dev wants to merge 2 commits intodevelopfrom
feature/article
Open

[Feature/article] 아티클 API 연동 구조 정리 및 다국어 자동 번역 적용#203
Yunil-dev wants to merge 2 commits intodevelopfrom
feature/article

Conversation

@Yunil-dev
Copy link
Copy Markdown
Contributor

📝 관련 문서 레퍼런스

- [Issue] : #202 
- [Slack] : 
- [Notion] : 

💻 주요 변경 사항은 무엇인가요?

- 기존 `/articles` 목록에 정적 아티클과 API 아티클을 함께 노출하도록 수정
- 기존 `/articles/[slug]` 상세에서 정적 아티클 우선 조회 후 API 아티클 상세를 fallback 으로 조회하도록 수정
- 공개 아티클 프론트 모델을 백엔드 응답 구조에 맞게 정리
- 아티클 목록/상세에서 `N분 읽기` 관련 필드와 UI 제거
- 백엔드 `summary` 를 상세 요약 본문으로 사용하도록 정리
- `summaryItems` 제거
- API 아티클 `image_layout` 값에 따라 상세 이미지 레이아웃이 달라지도록 반영
- 정적 아티클 상세 직렬화 에러가 나지 않도록 optional 필드 `undefined` 정리
- 정적 아티클 상세의 문단/불릿 UI 복원
- Gemini 기반 서버 번역 레이어 추가
- `/ko/articles` 는 원문 기준, `/en/articles` 는 API 아티클을 자동 번역해서 노출하도록 수정
- 아티클 번역 결과에 대한 서버 메모리 캐시 추가

📚 추가된 라이브러리

- [추가] : NO

📱 결과 화면 (선택)


🙇 코드 리뷰 중점사항, 예상되는 문제점 (선택)

-

@Yunil-dev Yunil-dev self-assigned this May 8, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
meditrip-web Ready Ready Preview, Comment May 8, 2026 5:01am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5972163-de0e-4afe-b3db-7baf34f6fc96

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/article

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

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

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 implements a comprehensive article management system, transitioning from static data to a hybrid model that supports backend-driven content. It introduces admin CRUD interfaces, updates public article pages to handle the new data structure, and adds server-side logic for fetching and AI-translating articles using Google Gemini. Feedback focuses on critical errors in the Gemini SDK integration, such as incorrect package and model names, as well as SSR consistency issues, potential memory leaks in the translation cache, and a usability flaw in the admin form that forces image re-uploads during edits.

Comment thread src/server/articles.ts
@@ -0,0 +1,437 @@
import { createHash } from 'node:crypto';
import { GoogleGenAI } from '@google/genai';
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

@google/genai 패키지는 공식 Google Generative AI SDK 명칭이 아닙니다. 공식 패키지인 @google/generative-ai를 의도하신 것이라면 임포트 경로와 클래스 사용법을 확인해 주시기 바랍니다.

Comment thread src/server/articles.ts

const FALLBACK_COVER_IMAGE = '/og/OG_image.jpg';
const ARTICLE_SOURCE_LOCALE: Locale = 'ko';
const ARTICLE_TRANSLATION_MODEL = 'gemini-2.5-flash';
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

gemini-2.5-flash는 존재하지 않는 모델명입니다. 현재 사용 가능한 모델인 gemini-1.5-flash 또는 gemini-2.0-flash 등으로 수정이 필요합니다. 잘못된 모델명은 API 호출 실패의 원인이 됩니다.

Suggested change
const ARTICLE_TRANSLATION_MODEL = 'gemini-2.5-flash';
const ARTICLE_TRANSLATION_MODEL = 'gemini-1.5-flash';

Comment thread src/server/articles.ts
contents: [{ role: 'user', parts: [{ text: prompt }] }],
});

const rawText = typeof result.text === 'string' ? result.text.trim() : '';
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

공식 SDK 기준으로 생성된 텍스트는 result.response.text() 메서드를 통해 가져와야 합니다. result.text를 직접 참조할 경우 값이 undefined가 되어 번역 결과가 정상적으로 반영되지 않을 수 있습니다.

Comment thread src/server/articles.ts
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
};

const normalizeCreatedAt = (value?: string | null) => value || new Date().toISOString();
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

normalizeCreatedAt 함수에서 값이 없을 때 new Date().toISOString()을 반환하고 있습니다. 이는 서버 사이드 렌더링(SSR) 시마다 값이 변하게 되어 데이터의 일관성을 해치고, SEO 및 캐싱에 부정적인 영향을 줄 수 있습니다. 고정된 기본값을 사용하거나 null을 유지하도록 처리하는 것이 좋습니다.

Suggested change
const normalizeCreatedAt = (value?: string | null) => value || new Date().toISOString();
const normalizeCreatedAt = (value?: string | null) => value || '';

Comment thread src/server/articles.ts
Comment on lines +112 to +113
const articleTranslationCache =
globalThis.__articleTranslationCache ?? (globalThis.__articleTranslationCache = new Map());
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

articleTranslationCache가 크기 제한이 없는 Map으로 구현되어 있습니다. 서버가 장기간 실행될 경우 메모리 사용량이 계속 증가하여 메모리 누수(Memory Leak)가 발생할 위험이 있습니다. lru-cache와 같은 라이브러리를 사용하거나, 최대 항목 수 및 TTL(Time To Live) 제한을 두는 것을 권장합니다.

Comment on lines +401 to +411
form.paragraphs.some(
(paragraph) =>
paragraph.existing_image_urls.length > 0 && paragraph.image_files.length === 0
)
) {
showToast({
title: '단락을 수정할 때는 기존 단락 이미지를 다시 업로드해야 합니다.',
icon: 'exclaim',
});
return;
}
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

단락의 텍스트만 수정하더라도 기존 이미지가 있다면 새 이미지 파일을 반드시 다시 업로드하도록 강제하고 있습니다. 이는 관리자가 단순한 오타 수정을 할 때도 매번 이미지를 다시 찾아 업로드해야 하는 큰 불편함을 초래합니다. 기존 이미지 URL을 유지하거나 백엔드에서 기존 데이터를 보존할 수 있는 구조로 개선이 필요합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant