[Refactor] 회원가입 API를 multipart/form-data 방식으로 변경#8
Conversation
Summary of ChangesHello @hyobin-yang, 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의 데이터 전송 방식을 "application/json"에서 "multipart/form-data"로 리팩토링합니다. 이를 통해 JSON 형식의 회원 정보와 이미지 파일을 하나의 요청으로 효율적으로 전송할 수 있게 되어, 사용자 프로필 이미지 처리 방식이 개선됩니다. Highlights
🧠 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. Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Code Review
회원가입 API를 multipart/form-data로 전환하는 리팩토링 작업을 잘 수행해주셨습니다. PR 설명에 변경 사항을 상세히 기술해주셔서 리뷰하기 수월했습니다. 코드 변경 사항은 전반적으로 좋아 보이지만, 몇 가지 개선할 점과 잠재적인 버그를 발견하여 리뷰 코멘트를 남겼습니다. 특히, 이미지 데이터 처리 로직에서 발생할 수 있는 런타임 에러를 수정하고, API 엔드포인트를 환경 변수로 관리하여 코드의 유지보수성을 높이는 것을 제안합니다. 또한, 디버깅용으로 추가된 console.log 구문은 프로덕션 배포 전에 제거하는 것이 좋겠습니다. 자세한 내용은 각 파일의 코멘트를 확인해주세요.
| if (profileImage) { | ||
| // base64 data URL을 Blob으로 변환 | ||
| const base64Data = profileImage.split(',')[1] || profileImage; // data:image/jpeg;base64, 부분 제거 | ||
| const byteCharacters = atob(base64Data); | ||
| const byteNumbers = new Array(byteCharacters.length); | ||
| for (let i = 0; i < byteCharacters.length; i++) { | ||
| byteNumbers[i] = byteCharacters.charCodeAt(i); | ||
| } | ||
| const byteArray = new Uint8Array(byteNumbers); | ||
| const blob = new Blob([byteArray], { type: 'image/jpeg' }); | ||
| formData.append('image', blob, 'profile.jpg'); | ||
| } |
There was a problem hiding this comment.
현재 Base64-인코딩된 이미지 데이터를 Blob으로 변환하는 로직에 몇 가지 문제가 있습니다:
- 런타임 에러 발생 가능성:
profileImage가 데이터 URL(data:image/...)이 아닌 일반 URL 문자열일 경우,atob()함수에서 에러가 발생하여 애플리케이션이 중단될 수 있습니다. 현재 코드에서는 기본 이미지로 설정할 때 일반 URL이 할당될 수 있어 이 문제가 실제로 발생할 수 있습니다. - 비효율성: 문자열을 순회하며
charCodeAt을 호출하는 방식은 큰 이미지의 경우 비효율적일 수 있습니다. - 하드코딩된 MIME 타입:
type: 'image/jpeg'로 MIME 타입을 하드코딩하고 있어, 사용자가 PNG 등 다른 형식의 이미지를 업로드할 경우 문제가 될 수 있습니다.
fetch API를 사용하면 이 로직을 더 안전하고 간결하게 개선할 수 있습니다. fetch는 데이터 URL을 직접 처리할 수 있으며, 올바른 MIME 타입을 가진 Blob 객체를 반환해줍니다.
if (profileImage && profileImage.startsWith('data:image')) {
// data: URL을 Blob으로 변환하는 더 효율적이고 안전한 방법입니다.
const response = await fetch(profileImage);
const blob = await response.blob();
formData.append('image', blob, 'profile.jpg');
}
| // 디버깅: 요청 데이터 확인 | ||
| console.log('=== 회원가입 요청 데이터 ==='); | ||
| console.log('name:', requestBody.name, 'length:', requestBody.name.length); | ||
| console.log('email:', requestBody.email); | ||
| console.log('password:', requestBody.password, 'length:', requestBody.password.length); | ||
| console.log('birthdate:', requestBody.birthdate); | ||
| console.log('nickname:', requestBody.nickname, 'length:', requestBody.nickname.length); | ||
| console.log('bio:', requestBody.bio, 'length:', requestBody.bio.length); | ||
| console.log('profileImage:', requestBody.profileImage); | ||
| console.log('JSON 데이터:', jsonData); | ||
| console.log('name:', jsonData.name, 'length:', jsonData.name.length); | ||
| console.log('email:', jsonData.email); | ||
| console.log('password:', jsonData.password, 'length:', jsonData.password.length); | ||
| console.log('birthdate:', jsonData.birthdate); | ||
| console.log('nickname:', jsonData.nickname, 'length:', jsonData.nickname.length); | ||
| console.log('bio:', jsonData.bio, 'length:', jsonData.bio.length); | ||
| console.log('profileImage:', profileImage ? '파일로 전송됨' : '없음'); | ||
| console.log('========================'); |
| console.log('========================'); | ||
|
|
||
| // 백엔드 API로 회원가입 요청 | ||
| const response = await fetch('https://classic-daramg.duckdns.org/auth/signup', { |
회원가입 API 변경사항
개요
회원가입 API가
application/json방식에서multipart/form-data방식으로 변경되었습니다. JSON 데이터와 이미지 파일을 하나의 FormData에 담아서 전송해야 합니다.변경 전후 비교
변경 전 (기존 방식)
변경 후 (새로운 방식)
주요 변경사항
1. FormData 사용
기존의 JSON 객체 대신
FormData객체를 사용합니다.2. JSON 데이터를 Blob으로 감싸기
이유:
@RequestPart가 JSON 데이터를 인식하려면type: "application/json"이 명시된 Blob이어야 합니다.[object Object]문자열로 변환되어 서버에서 파싱할 수 없습니다.3. 이미지 파일 처리
이미지는 base64 문자열이 아닌 **파일(Blob)**로 전송합니다.
4. Content-Type 헤더 제거
multipart/form-data를 사용할 때는 Content-Type 헤더를 명시하지 않습니다.브라우저가 자동으로
multipart/form-data와 boundary를 설정합니다.5. 필드 이름
백엔드의
@RequestPart와 일치하도록 필드 이름을 정확히 지정해야 합니다.구현 코드 위치
파일:
src/app/loginpage/register/profile/page.tsx함수:
handleNext()(약 98번째 줄부터)백엔드 요구사항
RequestPart 필드
signupRequest (필수)
SignupRequestDto(JSON)application/jsonimage (선택)
MultipartFileimage/jpeg또는 기타 이미지 타입required = false)SignupRequestDto 필드
주의사항
JSON 데이터는 반드시 Blob으로 감싸야 합니다.
type: 'application/json'명시 필수@RequestPart가 인식하지 못하면 에러 발생필드 이름은 정확히 일치해야 합니다.
signupRequest: JSON 데이터 필드image: 이미지 파일 필드Content-Type 헤더를 명시하지 않습니다.
multipart/form-data와 boundary를 설정합니다.이미지는 선택사항입니다.
image필드를 전송하지 않아도 됩니다.테스트 체크리스트
참고 자료
src/app/loginpage/register/profile/page.tsxPOST /auth/signup@RequestPart사용,multipart/form-data방식