feature: 인증사진 임의 업로드 기능 구현 및 게시글에 프로필 이미지, 사용자 닉네임 연결#46
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Summary of Changes
Hello @FlashingFuture, 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은 커뮤니티 게시글 기능의 핵심적인 개선 사항을 포함합니다. 사용자가 '인증' 게시글에 단일 이미지를 직접 업로드할 수 있도록 지원하고, 모든 게시글에 작성자의 프로필 정보(닉네임, 이미지)를 명확하게 연동하여 사용자 경험을 향상시키는 데 중점을 두었습니다. 이를 통해 게시글 작성 및 조회 시 이미지와 작성자 정보가 더욱 일관성 있게 표시됩니다.
Highlights
- 단일 이미지 업로드 기능 도입: 게시글에 여러 이미지를 첨부하는 대신 단일 이미지만 업로드할 수 있도록 API 요청 및 응답 타입이
imageUrls(배열)에서imageUrl(단일 문자열)로 변경되었습니다. 특히 '인증' 카테고리 게시글의 경우, 사용자가 갤러리 등에서 직접 이미지를 선택하여 업로드할 수 있는 기능이 추가되었습니다. - 게시글 작성자 정보 연동: 게시글 목록 및 상세 페이지에서 작성자의 프로필 이미지와 닉네임을 표시하기 위해
Post인터페이스에authorInfo객체가 추가되었으며, 관련 UI 컴포넌트들이 업데이트되었습니다. - 게시글 편집/작성 페이지 로직 개선: 게시글 편집/작성 페이지(
CommunityEdit)에서 '인증' 카테고리일 때는 이미지 업로드 UI를, '공유' 카테고리일 때는 기존의 항목 선택 픽커 UI를 각각 다르게 표시하도록 로직이 분리 및 개선되었습니다. 또한, 이미지 업로드 전 미리보기 및 실제 업로드 처리 로직이 추가되었습니다. - API 타입 및 매퍼 함수 업데이트: 서버 응답에서
imageUrl과imageUrls를 유연하게 처리하기 위한pickImageUrl헬퍼 함수가 추가되었고, 게시글 생성 및 업데이트 요청 시 이미지 처리 방식이 변경되었습니다.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| 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 issue 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
-
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. ↩
There was a problem hiding this comment.
Code Review
이번 PR은 '인증' 게시글에 대한 이미지 업로드 기능을 도입하고, 커뮤니티 기능 전반에 작성자의 프로필 정보(닉네임, 이미지)를 연동하는 변경 사항을 담고 있습니다. Presigned URL을 이용한 이미지 업로드 로직과 authorInfo 객체를 통한 데이터 모델 개선 등 핵심적인 구현은 잘 이루어졌습니다. 다만, 빌드를 중단시킬 수 있는 치명적인 문법 오류, '공유' 게시글의 이미지가 설정되지 않는 버그, 코드 정리 및 스타일 가이드 위반 사항(하드코딩된 값, 인라인 스타일) 등 몇 가지 수정이 필요한 부분이 있습니다. 이 문제들을 해결하면 새로운 기능의 안정성과 유지보수성이 크게 향상될 것입니다.
| display: grid; | ||
| place-items: center; | ||
| background: rgba(0, 0, 0, 0.35); | ||
| color: ${({ theme }) => theme.colors.surface} |
| const author = | ||
| 'author' in res | ||
| ? (res.author?.nickname ?? String(res.author?.id ?? '')) | ||
| : String((res as PostResCreate).authorId); |
There was a problem hiding this comment.
PostResBase에 authorInfo가 필수로 추가되면서 모든 PostRes 타입에 authorInfo가 보장됩니다. 하지만 mapPostResToEntity 함수 내 author 변수 할당 로직은 authorInfo를 사용하지 않고 과거의 복잡한 분기 처리를 그대로 유지하고 있습니다. 이로 인해 mapListItemToEntity와 로직이 달라져 혼란을 유발하고 잠재적인 버그의 원인이 될 수 있습니다.
mapListItemToEntity에서처럼 res.authorInfo.nickname을 사용하도록 로직을 단순화하는 것이 좋겠습니다.
| const author = | |
| 'author' in res | |
| ? (res.author?.nickname ?? String(res.author?.id ?? '')) | |
| : String((res as PostResCreate).authorId); | |
| const author = res.authorInfo.nickname; |
| const handlePick = (it: SelectableItem) => { | ||
| if (submitting) return; | ||
| onSelectItem?.(it.id); | ||
| if (category === 'SHARE') { | ||
| const rid = Number(it.id); | ||
| onChange({ routeId: Number.isNaN(rid) ? undefined : rid }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
SHARE 카테고리에서 항목을 선택했을 때 routeId만 onChange를 통해 업데이트되고, 해당 항목의 imageUrl은 업데이트되지 않고 있습니다. 이로 인해 SHARE 게시글 생성 시 imageUrl이 누락되는 버그가 발생할 수 있습니다.
handlePick 함수에서 onChange를 호출할 때, 선택된 아이템(it)의 imageUrl도 함께 전달하여 상태를 업데이트해야 합니다.
const handlePick = (it: SelectableItem) => {
if (submitting) return;
onSelectItem?.(it.id);
if (category === 'SHARE') {
const rid = Number(it.id);
onChange({
routeId: Number.isNaN(rid) ? undefined : rid,
imageUrl: it.imageUrl,
});
}
};
|
|
||
| // 1) presign | ||
| const presign = await getProofPresign({ | ||
| type: 'verify', |
There was a problem hiding this comment.
스타일 가이드에 따르면 매직 넘버나 문자열은 이름 있는 상수로 관리해야 합니다.1 'verify'라는 문자열이 하드코딩되어 있습니다. 이 값은 API 요청에 사용되는 중요한 값이므로, constants.ts와 같은 파일에 상수로 정의하여 사용하는 것이 좋습니다. 이렇게 하면 코드의 가독성과 유지보수성이 향상됩니다.
| type: 'verify', | |
| type: 'verify', // TODO: 상수로 분리 |
Style Guide References
Footnotes
-
상수는 하드코딩하지 말고
constants.ts또는theme.ts에 관리해야 합니다. ↩
| </li> | ||
| ))} | ||
| </ImageList> | ||
| {imageUrl && imageUrl !== '{}' && ( |
| type="file" | ||
| accept="image/*" | ||
| onChange={handlePickProofFile} | ||
| style={{ display: 'none' }} |
There was a problem hiding this comment.
| title: string; | ||
| content: string; | ||
| imageUrls?: string[]; | ||
| imageUrl: string; // 단일 이미지 URL |
There was a problem hiding this comment.
update를 할때는 null이 허용되는데, 생성될때는 null이 허용되지 않습니다.
이게 왜 그런걸까요?
| }>; | ||
|
|
||
| type AuthorObj = { id: number; nickname: string; avatarUrl?: string | null }; | ||
| type AuthorObj = { id: number; nickname: string; imageUrl?: string | null }; |
There was a problem hiding this comment.
여기 imageUrl이 왜 undefined일까요?
| return res.imageUrl; | ||
| if (Array.isArray(res.imageUrls) && res.imageUrls.length > 0) | ||
| return res.imageUrls[0]!; | ||
| return ''; |
There was a problem hiding this comment.
null이 더 어울리지 않을까요?
혹은 에러가 더 어울리지 않을까.
실제 위 케이스가 아닌 경우가 없을꺼라는것을 명시적이여야 한다고 생각합니다.
| if (typeof query.authorId === 'number') q.push(`authorId=${query.authorId}`); | ||
| if (typeof query.routeId === 'number') q.push(`routeId=${query.routeId}`); | ||
| if (query.sort) q.push(`sort=${encodeURIComponent(query.sort)}`); | ||
| if (typeof query.limit === 'number') q.push(`limit=${query.limit}`); |
There was a problem hiding this comment.
이쪽에서 왜 type에 따라 조건문이 필요한 이유가 무엇인가요?
| export type PresignReq = { | ||
| /** 예: 'community-proof' */ | ||
| type: string; | ||
| contentType: string; | ||
| size: number; | ||
| }; |
| <ProfileHeader | ||
| userName={author} | ||
| userName={authorInfo?.nickname ?? ''} | ||
| postDate={createdAt.slice(0, 10)} | ||
| imageUrl={`https://picsum.photos/48?random=${id}`} | ||
| imageUrl={authorInfo?.imageUrl ?? ''} | ||
| /> |
There was a problem hiding this comment.
여기서 null safe를 하샸는데 이유가 무엇인가요?
작업 내용