-
Notifications
You must be signed in to change notification settings - Fork 2
[feat] get group contain me #439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthrough제3자(Third-party) 그룹 OAuth 인증을 도입하고, 그룹 식별자를 문자열( Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as 사용자
participant UI as 앱(UI)
participant Auth as Groups Auth
participant Local as localStorage
participant API as Groups API
rect rgba(230,245,255,0.6)
User->>UI: "그룹으로 로그인" 클릭
UI->>Local: redirectPath 저장
UI->>Auth: thirdPartyAuth() → 브라우저 리다이렉트
end
User->>Auth: 인증/승인
Auth-->>UI: 콜백 리디렉트 /[lng]?code=XYZ
rect rgba(240,255,240,0.6)
UI->>Local: code 저장('thirdPartyCode')
UI->>UI: router.replace(redirectPath or /{lng}/home)
end
rect rgba(255,248,230,0.6)
UI->>Local: thirdPartyCode 조회
alt code 존재
UI->>API: POST /third-party/token (code)
API-->>UI: accessToken
UI->>Local: groupsAccessToken 저장
UI->>API: GET /third-party/userinfo (Bearer token)
API-->>UI: myGroups
UI-->>User: 그룹 목록 표시/선택
else code 없음
UI-->>User: 기존 상태(미로그인)
end
end
sequenceDiagram
autonumber
actor User as 사용자
participant Editor as NoticeEditor
participant Local as localStorage
participant Backend as Backend API
User->>Editor: 공지 작성 및 제출
Editor->>Editor: noticeToSubmit 생성 (group 객체 포함)
Editor->>Local: groupsAccessToken 조회(optional)
Editor->>Backend: POST /notice { ..., group }
Backend-->>Editor: 생성된 Notice (group: { id,... } | null)
Editor-->>User: 완료 처리
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/api/notice/notice.ts (1)
107-123: Detail 페이지에서groupId대신group객체 사용으로 수정 필요
src/app/[lng]/(with-page-layout)/(with-sidebar-layout)/notice/[id]/NoticeInfo.tsx의groupId필드를group.uuid로 교체하고, 더 이상 불필요해진getGroup(groupId)호출을 제거하세요. 작성(handle-notice-submit.ts) 경로는 이미group객체를 사용 중입니다.src/app/[lng]/write/handle-notice-submit.ts (1)
202-210: 영문 전용 공지 시 body가 undefined로 전송됩니다.
noticeLanguage === 'en'인 경우koreanBody가 없어body: koreanBody!가 런타임에서 빈 값이 됩니다. 서버가 필수로 body를 요구하면 바로 실패합니다. 아래처럼 언어에 따라 body를 선택하세요.const notice = await createNotice({ title, deadline, - body: koreanBody!, + body: noticeLanguage === 'en' + ? (englishBody as string) + : (koreanBody as string), images: imageKeys, tags: tagIds, category, group, }).catch(() => null);
🧹 Nitpick comments (19)
.env (1)
6-6: Groups API Base URL의 트레일링 슬래시 제거 제안경로 조합 시
//가 중복될 수 있어(예:${BASE_URL}/v1→...gistory.me//v1) 안정적으로 합치려면 트레일링 슬래시를 제거하는 편이 안전합니다.다음 변경을 고려해 주세요:
-NEXT_PUBLIC_GROUPS_API_URL=https://api.stg.groups.gistory.me/ +NEXT_PUBLIC_GROUPS_API_URL=https://api.stg.groups.gistory.me참고: dotenv-linter의 UnorderedKey 경고는 기능 영향은 없으며, 정렬은 팀 규칙에 맞춰 선택적으로 적용해도 됩니다.
.vscode/settings.json (1)
15-16: 포맷 온 세이브 활성화 OK — 팀 합의만 확인하면 됩니다프로젝트 전반에 Prettier 포맷팅이 자동 적용됩니다. 에디터 확장(Prettier by esbenp)이 필요하므로 온보딩 가이드에 명시되면 좋겠습니다.
src/api/notice/notice.ts (2)
55-59: Notice 응답의 group.id와 생성 입력의 group.uuid 불일치응답 타입은
group: { id: string }, 생성자 입력은group: { uuid: string }로 달라 혼동/실수 여지가 큽니다. 동일한 도메인 개념이면 내부 타입을 통일하고, API 계약과 다르면 매핑 계층을 두는 편이 안전합니다.한 가지 방식:
- 내부 폼/상태에서는
id로 통일- 전송 시에만
uuid로 매핑아래는 생성 요청 시 매핑하는 예시입니다(입력 타입을
id로 바꾸고 payload에서uuid로 변환):export const createNotice = async ({ title, deadline, body, images, tags, category, - group, + group, }: { title: string; deadline?: Date; body: string; images: string[]; tags: number[]; category: (typeof Category)[keyof typeof Category]; - group: { uuid: string; name: string; profileImageUrl: string | null } | null; + group: { id: string; name: string; profileImageUrl: string | null } | null; }): Promise<NoticeDetail> => { const groupsToken: string | null = localStorage.getItem('groupsAccessToken'); return ziggleApi .post( '/notice', { title, deadline, body, images, tags, category, - group: group ?? undefined, + group: group + ? { + uuid: group.id, + name: group.name, + profileImageUrl: group.profileImageUrl, + } + : undefined, },또는 백엔드 계약이 허용한다면 응답 타입의
group.id를group.uuid로 바꾸는 방법도 있습니다. 어느 쪽이 백엔드 표준인지 확인 부탁드립니다.
135-145: 헤더 주입 로직은 합리적 — 단, 미사용 import 정리 권장토큰이 없을 때 헤더를 생략하는 분기 처리는 적절합니다. 다만 현재 파일 상단의
getGroupsTokenimport가 미사용으로 보입니다(린트 경고 유발). 제거해 주세요.파일 상단에서 다음 줄을 삭제:
-import { getGroupsToken } from '../group/group';src/app/[lng]/write/handle-notice-submit.ts (1)
25-29: Group 타입 중복 정의를 피하고 공용 타입으로 통합하세요.여러 파일에서
{ uuid; name; profileImageUrl }형태를 반복 선언하고 있습니다. 공용 타입(예:GroupLite)을 한 곳에서 export해 사용하면 drift를 줄일 수 있습니다.src/app/components/shared/Zabo/Zabo.tsx (2)
5-5: 미사용 import 제거.
getGroup를 더 이상 사용하지 않습니다. 제거하여 번들 크기와 혼동을 줄이세요.-import { getGroup } from '@/api/group/group';
39-46: 디버그 로그 제거.서버 컴포넌트에서
console.log는 서버 로그를 오염시킵니다. 필요 시debug플래그로 조건부 출력하세요.- console.log('group', group);src/app/[lng]/page.tsx (1)
9-11: 불필요한 async 제거 및 다중 code 쿼리 대비.
async가 필요 없고,code가 배열로 들어오는 경우 첫 값을 안전히 사용하도록 처리하세요.-export default async function Home({ params: { lng }, searchParams }: Props) { - const code = searchParams.code; - return <ThirdParty code={code as string} lng={lng} />; +export default function Home({ params: { lng }, searchParams }: Props) { + const code = Array.isArray(searchParams.code) + ? searchParams.code[0] + : searchParams.code; + return <ThirdParty code={code as string} lng={lng} />;src/app/[lng]/write/noticeEditorActions.ts (2)
25-29: Group 타입 중복 정의 통합 제안.여기서도
{ uuid; name; profileImageUrl }가 반복됩니다. 공용 타입으로 분리해 import하는 형태로 정리하면 안전합니다.Also applies to: 45-46
65-73: 액션 명칭과 상태 필드의 불일치(SET_ACCOUNT→group).액션 타입이
SET_ACCOUNT인 반면 상태는group을 갱신합니다. 추후 혼선을 줄이기 위해SET_GROUP액션을 추가(또는 교체)하세요. 하위 호환을 위해 둘 다 처리하는 것을 권장합니다.| { - type: 'SET_ACCOUNT'; + type: 'SET_ACCOUNT'; group: { uuid: string; name: string; profileImageUrl: string | null; } | null; }; + | { + type: 'SET_GROUP'; + group: { + uuid: string; + name: string; + profileImageUrl: string | null; + } | null; + }; @@ - case 'SET_ACCOUNT': + case 'SET_ACCOUNT': + case 'SET_GROUP': return { ...state, group: action.group };Also applies to: 168-170
src/api/group/group.ts (2)
1-6: 불필요/부정확한 import 정리.
next/dist/server/api-utils의redirect는 잘못된 경로이고 사용되지도 않습니다. 또한ziggleApi도 미사용입니다. 제거하세요.-import axios from 'axios'; -import dayjs from 'dayjs'; -import { redirect } from 'next/dist/server/api-utils'; +import axios from 'axios'; +import dayjs from 'dayjs'; @@ -import { ziggleApi } from '..'; +// (no-op)
60-64: getGroup URL 정규화.
groupsUrl의 슬래시 유무와 무관하게 동작하도록 보정한webBase를 사용하세요.-export const getGroup = async (uuid: string): Promise<GroupInfo> => { - return axios - .get<GroupInfo>(`${groupsUrl}/group/${uuid}`) +export const getGroup = async (uuid: string): Promise<GroupInfo> => { + return axios + .get<GroupInfo>(`${webBase}/group/${uuid}`) .then(({ data }) => data); };src/app/[lng]/write/SelectAccountArea.tsx (6)
4-4: 불필요한 import 제거 (use)
use는 사용되지 않습니다. 제거해 번들 크기와 린트 경고를 줄이세요.-import { use, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react';
34-35: 불리언 원시 타입 사용
Boolean대신 원시 타입boolean을 사용하세요. 박싱된 객체 타입은 예기치 않은 비교/진리값 문제를 유발할 수 있습니다.-const [isModalOpen, setIsModalOpen] = useState<Boolean>(false); +const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
64-78: 하드코딩된 텍스트 i18n 적용 + setState 중복 호출 정리Swal 타이틀/본문은 i18n으로 치환하는 것이 좋습니다. 또한 confirm/else 모두에서
setIsModalOpen(false)가 호출되어 중복입니다.- Swal.fire({ - title: 'Redirecting', - text: 'Do you want to get group information?', + Swal.fire({ + title: t('groups.redirectingTitle'), + text: t('groups.redirectingBody'), icon: 'info', showCancelButton: true, }).then((result) => { - if (result.isConfirmed) { - thirdPartyAuth(path); - setIsModalOpen(false); - } - setIsModalOpen(false); + if (result.isConfirmed) { + thirdPartyAuth(path); + } + setIsModalOpen(false); });키 값은 예시입니다. 실제 키는 프로젝트 컨벤션에 맞춰 주입해 주세요.
17-21: 중복 타입 정의 제거: 기존 GroupInfo 활용동일 필드 서브셋이면 새 타입 정의 대신
Pick<GroupInfo, ...>를 사용해 단일 소스 유지가 좋습니다.-type Group = { - uuid: string; - name: string; - profileImageUrl: string | null; -}; +type Group = Pick<GroupInfo, 'uuid' | 'name' | 'profileImageUrl'>;
43-56: 사소한 정리: 불필요한 옵셔널 체이닝 제거
selectedGroup존재가 보장된 이후에는?.대신 직접 접근이 간결합니다.-const groupData = { - uuid: selectedGroup?.uuid, - name: selectedGroup?.name, - profileImageUrl: selectedGroup?.profileImageUrl || null, -}; +const groupData = { + uuid: selectedGroup.uuid, + name: selectedGroup.name, + profileImageUrl: selectedGroup.profileImageUrl ?? null, +};
46-46: 디버그 로깅 제거 권장
console.log는 남기지 않는 것이 좋습니다. 필요 시debug플래그나 로거 유틸을 사용하세요.- console.log('selectedGroup', selectedGroup); + // no-op- console.log('data', data); + // no-opAlso applies to: 96-96
src/app/[lng]/write/NoticeEditor.tsx (1)
672-681: 타입 중복 최소화: 공용 타입/서브셋 사용여기서도 인라인 객체 타입 대신
Pick<GroupInfo, 'uuid' | 'name' | 'profileImageUrl'> | null을 사용하면 중복을 줄일 수 있습니다. 액션 타입 명도SET_GROUP으로 바꾸면 의도 전달이 더 명확합니다(옵션).- setGroup={( - group: { - uuid: string; - name: string; - profileImageUrl: string | null; - } | null, - ) => dispatch({ type: 'SET_ACCOUNT', group })} + setGroup={(group: Pick<GroupInfo, 'uuid' | 'name' | 'profileImageUrl'> | null) => + dispatch({ type: 'SET_ACCOUNT', group }) + }추가 제안(별도 PR 권장):
- 액션 타입
SET_ACCOUNT→SET_GROUP리네이밍.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
.env(1 hunks).vscode/settings.json(1 hunks)src/api/group/group.ts(2 hunks)src/api/notice/notice.ts(3 hunks)src/app/[lng]/page.tsx(1 hunks)src/app/[lng]/thirdParty.tsx(1 hunks)src/app/[lng]/write/NoticeEditor.tsx(2 hunks)src/app/[lng]/write/SelectAccountArea.tsx(1 hunks)src/app/[lng]/write/handle-notice-submit.ts(3 hunks)src/app/[lng]/write/noticeEditorActions.ts(4 hunks)src/app/components/shared/Zabo/Zabo.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/app/[lng]/write/SelectAccountArea.tsx (2)
src/app/i18next/index.ts (1)
PropsWithT(65-65)src/api/group/group.ts (4)
GroupInfo(7-18)thirdPartyAuth(29-32)getGroupsToken(34-45)getMyGroups(47-58)
src/app/[lng]/page.tsx (2)
src/app/i18next/index.ts (1)
PropsWithLng(66-66)src/app/[lng]/thirdParty.tsx (1)
ThirdParty(7-16)
🪛 dotenv-linter (3.3.0)
.env
[warning] 6-6: [UnorderedKey] The NEXT_PUBLIC_GROUPS_API_URL key should go before the NEXT_PUBLIC_IDP_BASE_URL key
(UnorderedKey)
🔇 Additional comments (3)
src/app/[lng]/write/handle-notice-submit.ts (1)
45-47: 검증 완료: createNotice의 group 타입과 일치
createNotice 함수의 파라미터에서 group은{ uuid, name, profileImageUrl } | null로 정의되어 있어, 현재 전체 group 객체를 그대로 전달하는 방식이 타입 정의와 완전히 일치합니다. 별도 수정 불필요합니다.src/app/[lng]/write/SelectAccountArea.tsx (1)
72-74: 리다이렉트 키(redirectPath)로 통일
thirdPartyAuth 함수에서 localStorage 키를redirectPath로 변경해 전역 일관성을 유지하세요. 전체 코드베이스에서 해당 키 사용 현황을 검토해 다른 진입점에서 깨지지 않도록 확인해야 합니다.src/app/[lng]/write/NoticeEditor.tsx (1)
229-231: group 페이로드 타입 일치 확인, 추가 검증 불필요
APIcreateNotice의group파라미터 타입{ uuid, name, profileImageUrl } | null이 프론트엔드state.group와 완전히 일치합니다.
| groupsToken = await getGroupsToken(); | ||
| } | ||
|
|
||
| const groupsToken: string | null = localStorage.getItem('groupsAccessToken'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
SSR 안전성: API 레이어에서 localStorage 직접 접근
localStorage는 브라우저 전용입니다. API 모듈이 서버에서 호출될 가능성(예: Server Action/Route Handler)까지 고려하면 가드가 필요합니다.
-const groupsToken: string | null = localStorage.getItem('groupsAccessToken');
+const groupsToken: string | null =
+ typeof window !== 'undefined'
+ ? localStorage.getItem('groupsAccessToken')
+ : null;대안: 토큰을 호출부(클라이언트)에서 인자로 주입하거나, 공용 토큰 리졸버 함수를 만들어 환경에 따라 안전하게 읽도록 분리하는 방법도 있습니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const groupsToken: string | null = localStorage.getItem('groupsAccessToken'); | |
| // Guard against server-side execution: localStorage is only available in the browser | |
| const groupsToken: string | null = | |
| typeof window !== 'undefined' | |
| ? localStorage.getItem('groupsAccessToken') | |
| : null; |
🤖 Prompt for AI Agents
In src/api/notice/notice.ts around line 124, the code reads localStorage
directly which is unsafe for SSR; replace the direct access with a safe
approach: either accept the groupsAccessToken as a parameter from the caller
(preferred for API-layer purity) or call a shared token-resolver utility that
checks typeof window !== 'undefined' before reading localStorage and returns
null on server; update all callsites to pass the token when running on the
client or import the resolver and use it in place of localStorage.getItem to
avoid server-side errors.
…d translation for groups client login
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/app/[lng]/write/SelectAccountArea.tsx (2)
37-39: 렌더마다 반복 저장됨 — 의존성 배열 추가
useEffect에 의존성 미지정으로 매 렌더마다redirectPath가 덮어써집니다.[path]를 지정하세요.- useEffect(() => { - localStorage.setItem('redirectPath', path); - }); + useEffect(() => { + localStorage.setItem('redirectPath', path); + }, [path]);
81-95: 코드 교환 후 잔여 코드 제거
thirdPartyCode는 일회성입니다. 교환 성공 시 즉시 제거하세요(재시도 루프 방지).useEffect(() => { (async () => { if (thirdPartyCode) { const accessToken = await getGroupsToken(thirdPartyCode); if (accessToken) { localStorage.setItem('groupsAccessToken', accessToken); setAccessToken(accessToken); + localStorage.removeItem('thirdPartyCode'); } } })(); }, [thirdPartyCode]);src/app/[lng]/thirdParty.tsx (1)
10-18: 오픈 리다이렉트 방지 + redirectPath 정리 누락
외부/절대 URL이redirectPath에 들어오면 오픈 리다이렉트가 됩니다. 또한 사용 후redirectPath를 지우지 않아 재사용 위험이 있습니다.useEffect(() => { - if (code) { - localStorage.setItem('thirdPartyCode', code); - const path = localStorage.getItem('redirectPath'); - router.replace(path ? path : `/${lng}/home`); - } else { - router.replace(`/${lng}/home`); - } + const nextPath = localStorage.getItem('redirectPath'); + if (code && typeof code === 'string') { + localStorage.setItem('thirdPartyCode', code); + const safePath = + nextPath && /^\/(?!\/)/.test(nextPath) ? nextPath : `/${lng}/home`; + router.replace(safePath); + } else { + router.replace(`/${lng}/home`); + } + localStorage.removeItem('redirectPath'); }, [code, lng, router]);
🧹 Nitpick comments (8)
src/app/i18next/locales/ko/translation.json (1)
349-351: UI 문구 다듬기(자연스럽게)
표현을 간결하게 다듬으면 읽기성이 좋아집니다.- "loginGroupsClient": "그룹으로 공지를 작성하고 싶다면, 그룹스에 로그인해주세요" + "loginGroupsClient": "그룹으로 공지를 작성하려면 그룹스에 로그인해주세요"src/app/i18next/locales/en/translation.json (1)
361-363: 영문 문구 자연스러움/관사 수정
"a notice"와 브랜드명 전치사 사용을 정리하면 더 자연스럽습니다.- "loginGroupsClient": "If you want to write notice as a group, please log in to the Groups" + "loginGroupsClient": "To write as a group, please log in to Groups"src/api/group/group.ts (3)
68-72: 쿼리 파라미터 인코딩 방법 수정
파라미터 값에는encodeURIComponent를 사용해야 안전합니다.export const thirdPartyAuth = async () => { - window.location.href = `${groupsUrl}/thirdParty?client_id=${encodeURI( - clientId!, - )}&redirect_uri=${encodeURI(redirectUri!)}`; + window.location.href = `${groupsUrl}/thirdParty?client_id=${encodeURIComponent( + clientId! + )}&redirect_uri=${encodeURIComponent(redirectUri!)}`; };
74-86: 반환 타입 명시로 사용처 안정화
명시적 반환 타입을 지정해 호출부에서 분기 처리(문자열/널)를 확실히 하세요.-export const getGroupsToken = async (code: string) => { +export const getGroupsToken = async (code: string): Promise<string | null> => {
88-100: 반환 타입 명시
동일하게 반환 타입을 명시해 주세요.-export const getUserInfo = async (accessToken: string) => { +export const getUserInfo = async (accessToken: string): Promise<UserInfo | null> => {src/app/[lng]/write/SelectAccountArea.tsx (3)
34-34: 원시 타입 사용
Boolean대신boolean을 사용하세요.- const [isModalOpen, setIsModalOpen] = useState<Boolean>(false); + const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
66-80: 모달 문구 i18n 적용 + 불필요 의존성 제거
사용자 노출 문자열은 i18n 키를 사용하고,path는 의존성에서 제거해 재오픈을 방지하세요.- useEffect(() => { - isModalOpen && - Swal.fire({ - title: 'Redirecting', - text: 'Do you want to get group information?', - icon: 'info', - showCancelButton: true, - }).then((result) => { - if (result.isConfirmed) { - thirdPartyAuth(); - setIsModalOpen(false); - } - setIsModalOpen(false); - }); - }, [path, isModalOpen]); + useEffect(() => { + if (!isModalOpen) return; + Swal.fire({ + title: t('common.confirm'), + text: t('group.loginGroupsClient'), + icon: 'info', + showCancelButton: true, + }).then((result) => { + if (result.isConfirmed) { + thirdPartyAuth(); + } + setIsModalOpen(false); + }); + }, [isModalOpen, t]);
97-107: 변수명 섀도잉 해소 + 로그 정리
group(prop)과 지역 변수명이 겹칩니다. 의미를 드러내도록 변경하고, 콘솔 로그는 i18n 토스트 등으로 대체 고려.- const group = await getUserInfo(accessToken); - if (!group) { - console.log(`User do not belong to any group`); + const userGroups = await getUserInfo(accessToken); + if (!userGroups) { + // TODO: 사용자에게 안내 노출 //TODO: handle if user do not belong to any group return; } - setMyGroups(group); + setMyGroups(userGroups);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/api/group/group.ts(2 hunks)src/app/[lng]/page.tsx(1 hunks)src/app/[lng]/thirdParty.tsx(1 hunks)src/app/[lng]/write/SelectAccountArea.tsx(1 hunks)src/app/components/shared/Zabo/Zabo.tsx(2 hunks)src/app/i18next/locales/en/translation.json(1 hunks)src/app/i18next/locales/ko/translation.json(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/app/[lng]/page.tsx
- src/app/components/shared/Zabo/Zabo.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-18T11:59:04.320Z
Learnt from: dohyun-ko
PR: gsainfoteam/ziggle-fe#388
File: src/app/[lng]/(with-page-layout)/(without-sidebar-layout)/mypage/ClientActions.tsx:37-43
Timestamp: 2024-11-18T11:59:04.320Z
Learning: `ClientActions.tsx` 파일의 `handleWithdrawal` 함수에서 외부 URL을 열 때, 사용자에게 확인을 받지 않아도 됩니다.
Applied to files:
src/app/[lng]/thirdParty.tsx
📚 Learning: 2025-09-15T12:56:30.285Z
Learnt from: SHIM-JINSEO
PR: gsainfoteam/ziggle-fe#439
File: src/api/group/group.ts:24-27
Timestamp: 2025-09-15T12:56:30.285Z
Learning: In the ziggle-fe project, URL environment variables are standardized to always include trailing slashes, making URL concatenation safe without additional normalization.
Applied to files:
src/api/group/group.ts
🧬 Code graph analysis (1)
src/app/[lng]/write/SelectAccountArea.tsx (2)
src/app/i18next/index.ts (1)
PropsWithT(65-65)src/api/group/group.ts (4)
UserInfo(32-32)thirdPartyAuth(68-72)getGroupsToken(74-86)getUserInfo(88-100)
🔇 Additional comments (2)
src/api/group/group.ts (1)
18-20: 권한 타입 과도 제한 가능성(고정 튜플)
permissions: ['MEMBER_UPDATE']는 단일 값으로 고정됩니다. API가 가변 권한을 돌려준다면string[]로 넓혀야 합니다. 실제 응답 형태 확인 부탁드립니다.- permissions: ['MEMBER_UPDATE']; + permissions: string[];src/app/[lng]/write/SelectAccountArea.tsx (1)
53-57: 프로필 이미지: key ↔ url 혼용 가능성
profileImageUrl에profileImageKey를 그대로 넣고 있습니다. API가 실제 URL을 주는지 확인 필요합니다. 키만 온다면 URL 변환 헬퍼를 거치거나 타입을profileImageKey로 맞추세요.- const groupData = { + const groupData = { uuid: selectedGroup?.uuid, name: selectedGroup?.name, - profileImageUrl: selectedGroup?.profileImageKey || null, + // 확인 필요: API가 URL을 내려주면 아래처럼 교체 + // profileImageUrl: selectedGroup?.profileImageUrl ?? null, + // 키만 온다면 변환 함수 사용 예: + // profileImageUrl: selectedGroup?.profileImageKey ? getImageUrlFromKey(selectedGroup.profileImageKey) : null, };
Summary by CodeRabbit
New Features
Refactor
Chores