Skip to content

LC-2880 ADMIN 마그넷 내부화 글 등록 API 연동#2150

Open
hyonun321 wants to merge 2 commits intoLC-2838-Sprint-17from
LC-2880-ADMIN-마그넷-내부화-글-등록-API-연동

Hidden character warning

The head ref may contain hidden characters: "LC-2880-ADMIN-\ub9c8\uadf8\ub137-\ub0b4\ubd80\ud654-\uae00-\ub4f1\ub85d-API-\uc5f0\ub3d9"
Open

LC-2880 ADMIN 마그넷 내부화 글 등록 API 연동#2150
hyonun321 wants to merge 2 commits intoLC-2838-Sprint-17from
LC-2880-ADMIN-마그넷-내부화-글-등록-API-연동

Conversation

@hyonun321
Copy link
Contributor

@hyonun321 hyonun321 commented Feb 28, 2026

연관 작업

hyonun321 and others added 2 commits March 1, 2026 00:58
- magnetSchema.ts에 magnetDetailResponseSchema (magnetInfo + magnetQuestionInfo) 추가
- magnet.ts에 useGetMagnetDetailQuery, usePatchMagnetMutation 훅 추가
- PATCH 성공 시 detail + list 쿼리 동시 무효화

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MagnetPostDetail 타입을 API 응답 필드 기반으로 변경
- useMagnetPostForm에서 mock 제거, useGetMagnetDetailQuery로 데이터 로딩
- description JSON 파싱/직렬화로 metaDescription, programRecommend, magnetRecommend 매핑
- previewContents↔lexicalBefore, mainContents↔lexicalAfter 필드 매핑
- MagnetPostPage에서 initialData prop 제거, 로딩 스피너 추가
- page.tsx SSR fetch 제거
- mock.ts에서 post 관련 코드 제거

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hyonun321 hyonun321 self-assigned this Feb 28, 2026
@github-actions github-actions bot changed the title Lc 2880 admin 마그넷 내부화 글 등록 api 연동 LC-2880 ADMIN 마그넷 내부화 글 등록 API 연동 Feb 28, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 관리자 페이지의 마그넷 글 관리 기능을 백엔드 API와 통합하는 것을 목표로 합니다. 기존에 사용되던 목 데이터를 실제 API 호출로 대체하고, 데이터 스키마를 정의하여 데이터의 일관성과 유효성을 확보했습니다. 이를 통해 마그넷 글의 상세 조회 및 수정 프로세스가 실제 운영 환경에 맞춰 작동하도록 개선되었습니다.

Highlights

  • 마그넷 글 관리 API 연동: 마그넷 글 상세 조회 및 수정 기능을 위한 백엔드 API 연동이 완료되었습니다. 기존 목(mock) 데이터 대신 실제 API 호출을 통해 데이터를 가져오고 업데이트합니다.
  • React Query 훅 추가: 마그넷 상세 정보를 가져오는 useGetMagnetDetailQuery와 마그넷 글을 수정하는 usePatchMagnetMutation 훅이 추가되어 데이터 관리의 효율성을 높였습니다.
  • 마그넷 상세 스키마 정의: Zod를 사용하여 마그넷 상세 API 응답의 유효성을 검사하기 위한 magnetDetailResponseSchema 및 관련 스키마들이 새롭게 정의되었습니다.
  • 마그넷 글 작성/수정 페이지 리팩토링: 마그넷 글 작성/수정 페이지(MagnetPostPage)가 API로부터 직접 데이터를 로드하도록 변경되었으며, 로딩 상태 처리가 추가되었습니다. 또한, useMagnetPostForm 훅이 API 응답에 맞춰 폼 상태를 초기화하도록 개선되었습니다.
  • 목(Mock) 데이터 제거: 실제 API 연동에 따라 src/domain/admin/blog/magnet/mock.ts 파일에서 마그넷 글 관련 목 데이터 및 함수들이 모두 제거되었습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/api/magnet/magnet.ts
    • MagnetDetailResponse 및 magnetDetailResponseSchema 임포트 추가
    • magnetDetailQueryKey 상수 정의
    • 마그넷 상세 정보를 가져오는 useGetMagnetDetailQuery 훅 추가
    • 마그넷 업데이트 요청 바디를 위한 PatchMagnetReqBody 인터페이스 정의
    • 마그넷 글 수정을 위한 usePatchMagnetMutation 훅 추가 및 캐시 무효화 로직 구현
  • src/api/magnet/magnetSchema.ts
    • 마그넷 기본 정보를 위한 magnetInfoSchema 정의
    • 마그넷 질문 항목을 위한 magnetQuestionItemSchema 정의
    • 마그넷 질문을 위한 magnetQuestionSchema 정의
    • 마그넷 정보와 질문 정보를 포함하는 magnetDetailResponseSchema 정의
    • MagnetDetailResponse 타입 익스포트
  • src/app/admin/blog/magnet/[id]/post/page.tsx
    • fetchMagnetPost 임포트 및 호출 제거
    • MagnetPostPage 컴포넌트에서 initialData prop 제거
  • src/domain/admin/blog/magnet/MagnetPostPage.tsx
    • 로딩 상태 표시를 위한 CircularProgress 임포트
    • MagnetPostPageProps 인터페이스에서 initialData 속성 제거
    • useMagnetPostForm 훅에서 isLoading 상태를 받아 로딩 스피너 구현
    • MAGNET_TYPE 접근 시 MagnetTypeKey로 타입 캐스팅 추가
  • src/domain/admin/blog/magnet/hooks/useMagnetPostForm.ts
    • 목 데이터 saveMagnetPost 임포트 제거
    • useGetMagnetDetailQuery 및 usePatchMagnetMutation 임포트 추가
    • MagnetPostDetail 타입 임포트 제거
    • initialContent 파싱 로직을 description 필드의 JSON 파싱을 처리하는 parseDescription 함수로 리팩토링
    • useMagnetPostForm 훅이 magnetId를 숫자로 받도록 변경
    • useGetMagnetDetailQuery를 통합하여 데이터 페칭
    • usePatchMagnetMutation을 통합하여 글 저장 기능 구현
    • API에서 가져온 magnetInfo를 기반으로 formState, displayDate, endDate, content 상태 초기화 로직 구현
    • savePost 함수가 patchMagnet 뮤테이션을 사용하여 구조화된 description JSON과 함께 업데이트 요청을 보내도록 수정
    • isLoading, type, title을 magnetInfo에서 반환하도록 변경
  • src/domain/admin/blog/magnet/mock.ts
    • MagnetPostDetail, MagnetPostReqBody, MagnetTypeKey 임포트 제거
    • createMagnet, toggleMagnetVisibility, deleteMagnet 목 함수 제거
    • MOCK_MAGNET_POSTS, buildDefaultPost, fetchMagnetPost, saveMagnetPost 목 함수 제거
  • src/domain/admin/blog/magnet/types.ts
    • MagnetPostDetail 인터페이스를 새로운 API 응답 구조에 맞춰 업데이트 (metaDescription, thumbnail, displayDate, hasCommonForm, content 제거 및 description, previewContents, mainContents, desktopThumbnail, mobileThumbnail, startDate 추가)
    • MagnetPostReqBody 인터페이스 제거
Activity
  • hyonun321님이 마그넷 글 등록 API 연동을 위해 이 PR을 생성했습니다.
  • 기존 목 데이터를 실제 API 호출로 대체하는 코드 변경이 이루어졌습니다.
  • 마그넷 상세 조회 및 수정 기능을 위한 새로운 React Query 훅과 스키마가 추가되었습니다.
  • 프론트엔드 컴포넌트와 훅이 API 연동에 맞춰 리팩토링되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@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

이 PR은 어드민 마그넷 글 관리 기능에 실제 API를 연동하여 기존의 모의(mock) 데이터를 대체하는 작업을 담고 있습니다. React Query를 사용하여 데이터 페칭 및 뮤테이션을 구현한 점이 좋으며, 전반적으로 코드 구조가 잘 잡혀있습니다. 몇 가지 개선점을 제안합니다. Zod 스키마에서 더 엄격한 타입 검사를 위해 z.enum을 사용하고, React 훅에서 데이터 로드 후 상태를 초기화하는 로직을 useEffect로 리팩토링하여 React 모범 사례를 따르도록 하는 것이 좋겠습니다.

Comment on lines +49 to +53
questionType: z.string(),
isRequired: z.string(),
question: z.string(),
description: z.string().nullable(),
selectionMethod: z.string(),

Choose a reason for hiding this comment

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

medium

zod 스키마에서 questionType, isRequired, selectionMethod 필드를 z.string()으로 정의하셨습니다. 하지만 src/domain/admin/blog/magnet/types.ts에 정의된 관련 타입들을 보면 이 필드들은 특정 문자열 값만 허용하는 enum 형태입니다.

  • questionType: 'SUBJECTIVE' | 'OBJECTIVE'
  • isRequired: 'REQUIRED' | 'OPTIONAL'
  • selectionMethod: 'SINGLE' | 'MULTIPLE'

z.string() 대신 z.enum()을 사용하면 API 응답을 더 엄격하게 검증하고 타입 안정성을 높일 수 있습니다. 예상치 못한 값이 들어왔을 때 런타임 에러를 방지하는 데 도움이 됩니다.

Suggested change
questionType: z.string(),
isRequired: z.string(),
question: z.string(),
description: z.string().nullable(),
selectionMethod: z.string(),
questionType: z.enum(['SUBJECTIVE', 'OBJECTIVE']),
isRequired: z.enum(['REQUIRED', 'OPTIONAL']),
question: z.string(),
description: z.string().nullable(),
selectionMethod: z.enum(['SINGLE', 'MULTIPLE']),

Comment on lines +93 to +128
const [formState, setFormState] = useState<FormState>({
metaDescription: '',
thumbnail: '',
hasCommonForm: false,
});
const [formInitialized, setFormInitialized] = useState(false);

// detailData가 로드되면 폼 상태 초기화
if (magnetInfo && !formInitialized) {
setFormState({
metaDescription: descPayload.metaDescription,
thumbnail: magnetInfo.desktopThumbnail ?? '',
hasCommonForm: false,
});
setFormInitialized(true);
}

const [displayDate, setDisplayDate] = useState<Dayjs | null>(null);
const [endDate, setEndDate] = useState<Dayjs | null>(null);
const [dateInitialized, setDateInitialized] = useState(false);

if (magnetInfo && !dateInitialized) {
setDisplayDate(magnetInfo.startDate ? dayjs(magnetInfo.startDate) : null);
setEndDate(magnetInfo.endDate ? dayjs(magnetInfo.endDate) : null);
setDateInitialized(true);
}

const [content, setContent] = useState<MagnetPostContent>(
createEmptyContent(),
);
const [content, setContent] = useState<MagnetPostContent>(initialContent);
const [contentInitialized, setContentInitialized] = useState(false);

if (magnetInfo && !contentInitialized) {
setContent(initialContent);
setContentInitialized(true);
}

Choose a reason for hiding this comment

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

medium

데이터 로딩 후 폼 상태를 초기화하기 위해 렌더링 중에 if (magnetInfo && !...Initialized) 조건문과 useState를 함께 사용하는 패턴을 여러 번 사용하고 계십니다. 이는 React의 렌더링 로직 내에서 부수 효과(side effect)를 유발할 수 있어 안티패턴으로 간주됩니다.

이러한 상태 초기화 로직은 useEffect 훅으로 옮기는 것이 좋습니다. useEffect는 컴포넌트가 렌더링된 후에 실행되므로, 데이터 의존성을 명확히 하고 부수 효과를 안전하게 관리할 수 있습니다.

아래와 같이 하나의 useEffect로 관련 상태들을 한 번에 초기화하는 것을 권장합니다.

  const [formState, setFormState] = useState<FormState>({
    metaDescription: '',
    thumbnail: '',
    hasCommonForm: false,
  });
  const [displayDate, setDisplayDate] = useState<Dayjs | null>(null);
  const [endDate, setEndDate] = useState<Dayjs | null>(null);
  const [content, setContent] = useState<MagnetPostContent>(
    createEmptyContent(),
  );

  useEffect(() => {
    if (magnetInfo) {
      setFormState({
        metaDescription: descPayload.metaDescription,
        thumbnail: magnetInfo.desktopThumbnail ?? '',
        hasCommonForm: false,
      });
      setDisplayDate(magnetInfo.startDate ? dayjs(magnetInfo.startDate) : null);
      setEndDate(magnetInfo.endDate ? dayjs(magnetInfo.endDate) : null);
      setContent(initialContent);
    }
  }, [magnetInfo, descPayload, initialContent]);
References
  1. 스타일 가이드에서는 함수가 이름에서 암시하는 동작만 수행하고 숨겨진 부수 효과를 피해야 한다고 권장합니다(단일 책임 원칙). React 컴포넌트의 렌더링 로직 내에서 상태를 업데이트하는 것은 부수 효과에 해당하며, 이는 useEffect를 통해 관리하는 것이 바람직합니다. (link)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant