-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/#29 supabase api #32
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
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Skipped Deployment
|
Walkthrough이 PR은 Supabase API 연동을 위한 여러 기능을 추가 및 개선합니다. 새로운 API 파일들이 도입되어 식사 기록의 조회, 생성, 수정, 삭제와 이미지 업로드, 사용자 정보 조회 및 업데이트를 처리합니다. 또한, 객체의 케이스 변환, 파일명 안전 처리 유틸리티가 도입되고 DTO 및 타입 정의가 확장되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant MealAPI
participant Database
Client->>MealAPI: createMealWithDetails(mealData, mealDetails)
MealAPI->>Database: createMeal(meal)
Database-->>MealAPI: MealOverviewDTO
MealAPI->>Database: createMealDetails(mealId, mealDetails)
Database-->>MealAPI: MealDetailDTO[]
MealAPI-->>Client: Return MealDTO
sequenceDiagram
participant Client
participant StorageAPI
participant SupabaseStorage
Client->>StorageAPI: uploadImage(bucketName, formData)
StorageAPI->>SupabaseStorage: Upload file with unique filename
SupabaseStorage-->>StorageAPI: Response (data/error)
StorageAPI-->>Client: Return public URL / error response
sequenceDiagram
participant Client
participant UserAPI
participant Database
Client->>UserAPI: updateUser(userInfo)
UserAPI->>Database: Update user record (key conversion)
Database-->>UserAPI: Updated UserDTO
UserAPI-->>Client: Return transformed UserDTO
Assessment against linked issues
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 1
🧹 Nitpick comments (8)
src/types/DTO/user.dto.ts (1)
24-24: UpdateUserDTO 타입에 Partial 적용을 고려하세요.
UpdateUserDTO는 사용자 업데이트 작업에 필요한 필드를 잘 선택했습니다. 그러나 실제 업데이트 작업에서는 모든 필드가 항상 필요하지 않을 수 있습니다.Partial<Pick<UserDTO, ...>>형태로 사용하면 업데이트 시 모든 필드를 선택적으로 제공할 수 있어 더 유연한 API가 될 것입니다.-export type UpdateUserDTO = Pick<UserDTO, 'nickname' | 'profileImage' | 'gender' | 'dailyCaloriesGoal'>; +export type UpdateUserDTO = Partial<Pick<UserDTO, 'nickname' | 'profileImage' | 'gender' | 'dailyCaloriesGoal'>>;src/lib/apis/user.api.ts (2)
8-18: getUser 함수 구현이 잘 되었습니다.사용자 정보를 가져오는 함수가 잘 구현되었습니다. JSDoc을 통한 문서화, 오류 처리, 그리고 snake_case에서 camelCase로의 변환을 적절히 처리하고 있습니다.
단, Supabase 쿼리에서 어떤 컬럼을 선택할지 명시하지 않았습니다. 성능 향상을 위해 필요한 컬럼만 선택하는 것이 좋습니다.
- const { data, error } = await supabase.from('users').select().single(); + const { data, error } = await supabase.from('users').select('id, email, nickname, profile_image, gender, daily_calories_goal, created_at').single();
20-33: updateUser 함수에 사소한 오타가 있습니다.사용자 정보를 업데이트하는 함수가 전반적으로 잘 구현되었습니다. 인증된 사용자 ID를 가져와서 해당 레코드를 업데이트하는 로직이 적절합니다.
JSDoc에 작은 오타가 있습니다: "변경 이수 수정된 값" -> "변경 이후 수정된 값"으로 수정하면 좋겠습니다.
또한 getUser 함수와 마찬가지로, select 문에서 필요한 컬럼만 명시하는 것이 성능상 더 좋습니다.
- const { data, error } = await supabase.from('users').update(userInfoSnakeCase).eq('id', userId).select().single(); + const { data, error } = await supabase.from('users').update(userInfoSnakeCase).eq('id', userId).select('id, email, nickname, profile_image, gender, daily_calories_goal, created_at').single();
- JSDoc 수정:
- * @returns {Promise<UserDTO>} 변경 이수 수정된 값 + * @returns {Promise<UserDTO>} 변경 이후 수정된 값src/lib/apis/storage.api.ts (1)
19-27: 파일 업로드 구현이 잘 되어 있습니다만, 파일 유효성 검사를 추가하면 좋겠습니다.UUID와 sanitizeFilename을 사용한 파일명 생성 방식은 좋습니다. 그러나 파일 타입이나 크기에 대한 유효성 검사가 없습니다. 보안과 성능을 위해 이러한 검사를 추가하는 것이 좋겠습니다.
export const uploadImage = async ( bucketName: SupabaseBucketValue, formData: FormData ): Promise<ErrorResponse<string>> => { const supabase = getServerClient(); const file = formData.get('file') as File; + // 파일 타입 및 크기 검증 + const validImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!validImageTypes.includes(file.type)) { + return { data: null, error: '지원되지 않는 이미지 형식입니다.' }; + } + + // 최대 파일 크기 제한 (5MB) + const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB + if (file.size > MAX_FILE_SIZE) { + return { data: null, error: '파일 크기는 5MB를 초과할 수 없습니다.' }; + } + const filename = crypto.randomUUID() + sanitizeFilename(file.name);src/lib/utils/filename.util.ts (1)
39-75: 파일명 정제 함수가 철저하게 구현되어 있습니다.입력 유효성 검사, 안전하지 않은 문자 대체, 공백 제거, 연속된 대체 문자 압축, 시작/끝 대체 문자 제거, 빈 문자열 처리 등 다양한 엣지 케이스를 처리하고 있습니다. 각 단계에 대한 주석도 이해하기 쉽게 작성되어 있습니다.
다만, 성능 최적화를 위해 정규식을 미리 컴파일해두는 것을 고려해볼 수 있습니다.
export function sanitizeFilename(filename: string, replacement: string = '_'): string { if (!filename || typeof filename !== 'string') { return ''; } const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/!-.*'() &$@=;:+,?"; + // 미리 정규식 패턴 컴파일 + const regexPattern = new RegExp(`\\${replacement}{2,}`, 'g'); + const startEndPattern = new RegExp(`^\\${replacement}|\\${replacement}$`, 'g'); + let sanitized = ''; for (const char of filename) { sanitized += safeChars.includes(char) ? char : replacement; } // 시작과 끝의 공백 제거 sanitized = sanitized.trim(); // 연속된 대체 문자를 하나로 압축 (예: '___' -> '_') - const regexPattern = new RegExp(`\\${replacement}{2,}`, 'g'); sanitized = sanitized.replace(regexPattern, replacement); // 파일명 시작/끝에 있는 대체 문자 제거 - const startEndPattern = new RegExp(`^\\${replacement}|\\${replacement}$`, 'g'); sanitized = sanitized.replace(startEndPattern, ''); // 빈 문자열이 되면 기본값 설정 if (sanitized === '') sanitized = 'file'; return sanitized; }src/lib/utils/camelize.util.ts (1)
15-36: snakeToCamelObject의 제네릭 및 타입 변환 방식 주의 권장
객체, 배열, 원시 타입 등 다양한 케이스를 재귀적으로 처리하는 로직이 깔끔합니다. 다만,as unknown as CamelCaseObject<T>같은 형태의 캐스팅은 가독성을 떨어뜨릴 수 있으므로, 가능하다면 좀 더 명확한 타입 유추 방법을 고려하시는 것이 좋겠습니다.src/lib/apis/meal.api.ts (2)
48-60: 날짜 쿼리 범위 설정이 다소 모호할 수 있음
getMyMealByDate함수에서T24:00:00Z를 사용하는 방식은 일반적으로2023-10-15T23:59:59.999Z처럼 설정하거나, 다음 날 00시로 범위를 지정하는 방식을 쓰는 게 명확합니다. 날짜 경계 처리와 관련된 혼동을 예방하려면 아래와 같이 변경을 권장합니다.- .lt('ate_at', `${date}T24:00:00Z`) + .lt('ate_at', `${date}T23:59:59.999Z`)
122-129: 트랜잭션 고려 추천: createMealWithDetails에서 원자성 보장
createMealWithDetails함수는 식사 생성 후 상세 정보를 생성하는 순차 로직이므로, 중간에 오류가 난 경우 데이터 정합성이 깨질 가능성이 있습니다. 가능한 경우 Supabase의 트랜잭션(또는 관련 기능)을 사용해 원자성을 보장하는 방안을 고려해 보세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
src/lib/apis/meal.api.ts(1 hunks)src/lib/apis/storage.api.ts(1 hunks)src/lib/apis/user.api.ts(1 hunks)src/lib/utils/camelize.util.ts(2 hunks)src/lib/utils/filename.util.ts(1 hunks)src/types/DTO/meal.dto.ts(1 hunks)src/types/DTO/user.dto.ts(2 hunks)src/types/common.type.ts(2 hunks)src/types/meal-type.type.ts(1 hunks)src/types/supabase-bucket.type.ts(1 hunks)
🔇 Additional comments (20)
src/types/supabase-bucket.type.ts (2)
1-4: 타입 상수 정의가 명확합니다.Supabase 버킷 이름을 상수로 정의하여 코드 전체에서 일관되게 사용할 수 있도록 한 것이 좋습니다.
as const어서션을 사용하여 타입 안전성을 보장한 점도 적절합니다.
6-7: 타입 유틸리티 활용이 효과적입니다.TypeScript의 유틸리티 타입을 활용하여
SupabaseBucket상수로부터 키와 값 타입을 파생시킨 방식이 효율적입니다. 이는 코드의 타입 안전성과 유지보수성을 높여줍니다.src/types/DTO/user.dto.ts (1)
9-10: 사용자 DTO에 새 필드 추가가 적절합니다.성별(
gender)과 일일 칼로리 목표(dailyCaloriesGoal) 필드를 추가하고 null 타입을 허용하여 선택적 필드로 만든 것이 적절합니다. 이는 기존 필드 스타일과도 일관성이 있습니다.src/types/common.type.ts (2)
12-12:Details
❓ Verification inconclusive
SnakeCaseObject 타입 간소화의 영향을 확인하세요.
문자열 변환 로직을 제거하여 타입 정의를 간소화했습니다. 이전에는 문자열 타입에 대해 특별한 변환을 적용했지만, 현재는 비 객체 타입에 대해 단순히
T를 반환합니다. 이 변경이 의도적이라면 문제가 없지만, 문자열 값에 대한 변환이 필요하다면 기존 코드에 영향을 줄 수 있습니다. 현재 코드베이스에서 이 타입이 어떻게 사용되는지 확인하세요.
🏁 Script executed:
#!/bin/bash # SnakeCaseObject 타입이 사용되는 위치 확인 rg "SnakeCaseObject" --type tsLength of output: 1177
검토 요청: SnakeCaseObject 타입 변경 영향 재확인
변경 사항:
- 문자열 타입에 대해 별도의 변환 로직이 제거되어, 이제 비 객체 타입에서 단순히
T를 반환합니다.영향 분석:
- 해당 타입은 여러 DTO 파일(
src/types/DTO/user.dto.ts,src/types/DTO/meal.dto.ts등)과camelize관련 유틸(src/lib/utils/camelize.util.ts)에서 사용되고 있습니다.- 만약 기존 문자열 변환 로직이 의도한 동작이었거나, 중요한 역할을 했다면 이 변경이 예상치 못한 부작용을 일으킬 수 있으므로, 변경 전후의 동작이 동일한지 반드시 재검토해 주세요.
23-23:Details
❓ Verification inconclusive
CamelCaseObject 타입 간소화의 영향을 확인하세요.
SnakeCaseObject와 마찬가지로, CamelCaseObject 타입에서도 문자열 변환 로직이 제거되었습니다. 이 변경이 의도적인지, 그리고 기존 코드에 영향을 주지 않는지 확인하는 것이 좋습니다.
🏁 Script executed:
#!/bin/bash # CamelCaseObject 타입이 사용되는 위치 확인 rg "CamelCaseObject" --type tsLength of output: 792
CamelCaseObject 타입 간소화 영향 재확인 요청
CamelCaseObject 타입에서 문자열 변환 로직이 제거된 부분이 SnakeCaseObject와 일관되게 의도적으로 간소화된 것으로 보입니다.
rg 검색 결과에 따르면, 해당 타입은 주로
- src/types/common.type.ts 에서 정의되어 있고,
- src/lib/utils/camelize.util.ts 등에서 타입 캐스팅 목적으로 사용되고 있음을 확인했습니다.
따라서 변경 사항이 전반적으로 반영된 것으로 보이나, 기존 코드에서 이 타입이 원래 기대했던 문자열 변환 효과를 필요로 했던 부분은 없는지 추가 확인해 주시기 바랍니다.
src/lib/apis/user.api.ts (1)
1-7: 서버 함수 설정과 임포트가 적절합니다.
'use server'지시어를 사용하여 Next.js 서버 함수로 명시한 것이 적절합니다. 필요한 모듈들을 적절히 임포트하고 있습니다.src/types/meal-type.type.ts (3)
1-18: 식사 유형 상수 맵이 잘 정의되어 있습니다!식사 유형을 객체로 정의하고
as const를 사용하여 읽기 전용으로 만든 것은 좋은 방식입니다. 각 타입이 value와 label을 가지고 있어 데이터베이스 저장값과 표시값을 명확히 구분했습니다.
20-22: 타입 정의가 명확하게 되어 있습니다.TypeScript의 유틸리티 타입을 효과적으로 활용하여 다양한 타입을 정의했습니다. 이러한 방식으로 타입 안전성을 확보할 수 있습니다.
24-24: MealTypeArray 생성 방식이 효율적입니다.Object.values()를 사용하여 MealTypeMap에서 배열을 생성하는 방식은 효율적이고 유지보수에 용이합니다. 식사 유형이 추가되거나 변경될 때 자동으로 반영됩니다.
src/lib/apis/storage.api.ts (2)
8-18: 문서화가 상세하게 잘 되어 있습니다.JSDoc 주석을 통해 함수의 목적, 매개변수, 반환값을 명확하게 설명하고 있습니다. 이는 API 사용자가 함수를 이해하는 데 큰 도움이 됩니다.
28-32: 오류 처리가 적절하게 구현되어 있습니다.업로드 오류를 확인하고 적절하게 처리하는 방식이 좋습니다. 성공 시에는 publicUrl을 반환하고, 실패 시에는 오류 정보를 반환하는 일관된 구조를 사용하고 있습니다.
src/lib/utils/filename.util.ts (1)
1-37: 파일명 유효성 검사 함수가 잘 구현되어 있습니다.S3 객체 키로 사용 가능한 안전한 문자에 대한 상세한 설명과 함께 정규 표현식을 사용한 검증 방식이 적절합니다. AWS S3 문서를 참조한 것도 좋은 접근입니다.
src/types/DTO/meal.dto.ts (4)
1-13: MealDetailDTO가 잘 정의되어 있습니다.식사 상세 정보에 대한 데이터 구조가 명확하게 정의되어 있습니다. 영양 정보(칼로리, 탄수화물, 단백질, 지방)와 함께 중량까지 포함하고 있어 완전한 식사 상세 정보를 표현할 수 있습니다.
15-29: MealDTO 구조가 잘 설계되어 있습니다.식사 전체 정보를 포함하는 DTO가 완전하게 정의되어 있습니다. 기본 정보(ID, 생성일, 사용자 ID), 식사 정보(이미지, 시간, 타입, 메모), 영양 정보 합계, 그리고 상세 메뉴 목록까지 포함하고 있어 식사 데이터를 충분히 표현합니다.
31-33: 파생 타입들이 효율적으로 정의되어 있습니다.TypeScript의 유틸리티 타입(Omit, Pick)을 사용하여 기본 DTO에서 필요한 형태로 파생 타입을 만든 것은 효율적이고 타입 안전성을 보장하는 좋은 방식입니다. 특히 CreateMealDTO, CreateMealDetailDTO와 같이 생성 시 필요한 필드만 선택한 것이 적절합니다.
35-37: 데이터베이스 연동을 위한 스네이크 케이스 타입 변환이 적절합니다.SnakeCaseObject 유틸리티 타입을 사용하여 카멜 케이스 DTO를 스네이크 케이스로 변환한 것은 데이터베이스와의 연동을 위해 좋은 접근법입니다. 프론트엔드에서는 카멜 케이스를, 데이터베이스에서는 스네이크 케이스를 사용하는 일반적인 패턴을 잘 구현했습니다.
src/lib/utils/camelize.util.ts (2)
3-9: snakeToCamel 함수 로직 검토: 정상 동작으로 보입니다.
함수 구현부와 주석 모두 명확합니다. snake_case에서 camelCase로 단순 변환하는 상황에 충분히 적합해 보이며, 현재 사용 사례에는 문제 없을 듯합니다.
38-44: camelToSnake 함수 문법은 적절하나, 대문자 시작 문자열 고려 필요
str.replace(/[A-Z]/g, ...)패턴은 선행 문자가 없더라도 대문자가 나오면_가 붙습니다. 예를 들어"Hello"→"_hello"형태로 변환될 수 있습니다. 의도된 동작인지 다시 한 번 확인 부탁드립니다.src/lib/apis/meal.api.ts (2)
24-39: 결과가 비어 있는 경우 처리 검토
getAllMyMealsByPeriod함수에서 Supabase 쿼리 결과data가 빈 배열이거나 null일 때도 문제없이snakeToCamelObject가 처리 가능한지 점검해 주세요. 빈 객체나 null 반환 상황에서 호출 측이 정상적으로 대응할 수 있는지도 고려가 필요합니다.
153-169: 외래 키 제약 여부 확인
deleteMeal및deleteMealDetail가 참조 무결성을 어떻게 처리하는지 확인이 필요합니다. 만약 외래 키 관계가 걸려 있고 CASCADE 동작이 없으면 에러가 발생하거나 불필요한 쓰레기 데이터가 남을 수 있습니다.
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
🧹 Nitpick comments (3)
src/types/gender.type.ts (1)
1-10: 기본 Gender 상수 정의가 잘 되어 있습니다.Gender 상수에 MAN과 WOMAN만 정의되어 있는데, 현대 사회에서는 보다 다양한 젠더 정체성이 존재합니다. 프로젝트 요구사항에 따라 더 많은 옵션을 추가하거나 NON_BINARY, OTHER 등의 옵션을 고려해 볼 수 있을 것 같습니다.
또한 국제화를 고려한다면, 하드코딩된 한국어 레이블 대신 다국어 지원을 위한 구조를 고려해 보는 것도 좋을 것 같습니다.
src/lib/utils/camelize.util.ts (2)
38-44: camelToSnake 함수에서 대문자 처리 시 엣지 케이스 고려 필요현재 구현은 대부분의 camelCase 문자열을 올바르게 변환하지만, 두 가지 엣지 케이스를 처리하지 못합니다:
- 첫 글자가 대문자인 경우 (예: "UserData" → "_user_data") 불필요한 선행 밑줄이 추가됩니다.
- 연속된 대문자(예: "HTTPRequest")는 각 글자마다 밑줄이 추가되어 "h_t_t_p_request"와 같이 변환됩니다.
다음과 같이 개선할 수 있습니다:
-export const camelToSnake = (str: string): string => str.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`); +export const camelToSnake = (str: string): string => { + // 첫 글자가 대문자인 경우 특별 처리 + const firstCharHandled = str.charAt(0).toLowerCase() + str.slice(1); + // 나머지 대문자 처리 + return firstCharHandled.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`); +};
50-67: camelToSnakeObject 함수 구현에서 일관성 개선 필요현재 구현은 올바르게 작동하지만,
snakeToCamelObject와 비교했을 때 몇 가지 일관성 문제가 있습니다:
함수 선언 스타일:
snakeToCamelObject는 화살표 함수인 반면,camelToSnakeObject는 함수 선언문을 사용합니다.null과 undefined 처리:
snakeToCamelObject는null || undefined를 명시적으로 검사하지만,camelToSnakeObject는null만 검사합니다.제네릭 타입 제약:
camelToSnakeObject<T extends object>는 타입 제약이 있지만,snakeToCamelObject<T>는 제약이 없습니다.일관성을 위해 다음과 같이 수정을 고려해 보세요:
-export function camelToSnakeObject<T extends object>(obj: T): SnakeCaseObject<T> { - if (obj === null || typeof obj !== 'object') { +export const camelToSnakeObject = <T>(obj: T): SnakeCaseObject<T> => { + if (obj === null || obj === undefined) { return obj; } if (Array.isArray(obj)) { return obj.map((item) => camelToSnakeObject(item)) as unknown as SnakeCaseObject<T>; } + + if (typeof obj !== 'object') { + return obj as SnakeCaseObject<T>; + } const result: Record<string, unknown> = {}; for (const [key, value] of Object.entries(obj)) { const newKey = camelToSnake(key); if (typeof value === 'object') result[newKey] = camelToSnakeObject(value); else result[newKey] = value; } return result as SnakeCaseObject<T>; -} +};
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/lib/utils/camelize.util.ts(2 hunks)src/types/DTO/user.dto.ts(2 hunks)src/types/gender.type.ts(1 hunks)src/types/meal-type.type.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/types/DTO/user.dto.ts
🔇 Additional comments (8)
src/types/gender.type.ts (2)
12-14: TypeScript 타입 정의가 올바르게 되어 있습니다.TypeScript의
typeof및 인덱싱 타입을 활용하여 타입을 정의한 방식이 매우 효율적입니다. 이러한 접근 방식은Gender객체가 변경될 때 타입도 자동으로 업데이트되므로 유지보수성이 높습니다.
16-16: genderArray 배열 정의가 유용합니다.
Object.values(Gender)를 사용하여 배열을 생성하는 방식은 드롭다운이나 라디오 버튼 등의 UI 컴포넌트에서 모든 젠더 옵션을 쉽게 표시할 수 있게 해주어 매우 실용적입니다.src/types/meal-type.type.ts (3)
1-18: 잘 정의된 식사 유형 맵 구조입니다!
MealTypeMap은 잘 구성되어 있으며as const어설션을 사용하여 타입 안전성을 보장한 것이 좋습니다. 각 식사 유형에 대한 값과 라벨의 매핑이 명확합니다. 코드 주석도 값의 용도를 설명하는 데 도움이 됩니다.다국어 지원이 필요한 경우에는 향후 라벨 구조를
{ ko: '아침', en: 'Breakfast' }형태로 확장하는 것도 고려해볼 수 있습니다.
20-22: 타입스크립트 타입 정의가 잘 되어 있습니다.TypeScript의 indexed access types를 적절히 활용하여 타입을 정의하셨습니다. 이 방식은
MealTypeMap이 변경되면 자동으로 타입이 업데이트되어 유지보수에 용이합니다. 명명 규칙도 명확하고 일관성 있게 작성되었습니다.
24-24: 배열 변환이 효과적으로 구현되었습니다.
Object.values()를 사용하여MealTypeMap의 값들을 배열로 변환한 접근 방식이 좋습니다. 이 배열은 UI 렌더링이나 반복 작업에 유용하게 활용될 수 있으며, 타입 어노테이션MealTypeItem[]도 정확히 지정되어 있습니다.src/lib/utils/camelize.util.ts (3)
1-9: 타입 임포트 및 snakeToCamel 함수 구현이 적절합니다.
snakeToCamel함수는 snake_case 문자열을 camelCase로 변환하는 기능을 정확히 수행하고 있습니다. JSDoc 주석도 명확하게 작성되었습니다.
10-36: snakeToCamelObject 함수가 올바르게 구현되었습니다.함수는 객체의 모든 키를 snake_case에서 camelCase로 재귀적으로 변환합니다. null, undefined, 배열 및 중첩 객체도 적절히 처리합니다.
62-62: 이전에 수정된 버그가 정확히 해결되었습니다.이전 리뷰에서 지적된 재귀 호출 문제(
camelToSnakeObject함수 내에서snakeToCamelObject를 잘못 호출하는 문제)가 올바르게 수정되었습니다.
프롬프트의 해당 형식으로 음식 데이터를 받아올 수 있으면 좋겠네요. 프롬프트 작성시 참고사항! |
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
🧹 Nitpick comments (1)
src/types/DTO/user.dto.ts (1)
1-40: 전반적인 DTO 구조가 잘 설계되었습니다.사용자 정보 관리를 위한 DTO 타입들이 전체적으로 잘 구성되어 있습니다. 새로 추가된 필드들은 PR 목표에 명시된 사용자 정보 관리 API 기능을 잘 지원할 것입니다. 타입스크립트의 유틸리티 타입을 활용해 코드 중복 없이 타입을 확장한 점이 좋습니다.
한 가지 제안으로, 각 필드에 대한 JSDoc 주석을 추가하여 각 속성의 목적과 사용법을 명확히 문서화하는 것이 도움이 될 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/types/DTO/user.dto.ts(2 hunks)
🔇 Additional comments (4)
src/types/DTO/user.dto.ts (4)
3-3: 새로운 타입 임포트가 적절하게 추가되었습니다.사용자 DTO에 성별 정보를 추가하기 위해 GenderKey 타입을 임포트하는 것은 적절합니다. 타입 안전성을 확보하는 좋은 방법입니다.
7-7: 생성 시간 필드가 적절하게 추가되었습니다.사용자 정보에 createdAt 필드를 추가하는 것은 데이터 추적 및 관리에 유용합니다. Supabase와의 통합에도 적합합니다.
11-15: 사용자 프로필 정보 필드가 잘 정의되었습니다.건강 및 개인 정보 관련 필드(gender, dailyCaloriesGoal, height, weight, age)가 추가되었습니다. 모두 null을 허용하여 필수값이 아닌 선택적 정보로 처리한 것이 적절합니다. 이는 사용자가 모든 정보를 즉시 제공하지 않아도 되게 하는 유연성을 제공합니다.
28-28: 업데이트 DTO 타입이 잘 설계되었습니다.
UpdateUserDTO타입이UserDTO에서 적절하게 파생되었습니다.id,createdAt,
jiyxxn
left a 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.
토끼가 뛰노는 코드 숲에 새 기능 피어났네,
API들과 함께 즐겁게 당근을 씹으며,
식사, 이미지, 사용자 모두 반겨주네,
안전한 유틸과 DTO가 빛나는 밤,
코드의 리듬에 맞춰 한바탕 신나게 춤추네! 🐇✨
| .from('meals') | ||
| .select(` *, meal_details (*) `) | ||
| .gte('ate_at', `${date}T00:00:00Z`) | ||
| .lt('ate_at', `${date}T24:00:00Z`) |
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.
24:00:00 으로 사용하려면 date가 아니라 다음 날이 되어야 하지 않나요 ?! ${date}T23:59:59.999Z 으로 들어가야 하는 것 아닌지 여쭤봅니다!
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.
해당 부분의 조건문이 equal이 없는 less than이기 때문에 괜찮습니다!
지윤님 말씀대로 ${date}T23:59:59.999Z를 작성하고, lte를 사용하는 것과 동일하게 동작합니다!
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.
그런가요..? 제가 찾아본 바에 따르면 표준 시간 형식(ISO 8601 포함)에서는 시간을 00:00:00부터 23:59:59까지 표현해서 24:00:00이라는 표기는 다음 날에 해당한다고 해서 여쭤봤습니다! 제가 생각할 때
25년 4월 4일의 00시는 23시 59분보다 더 앞시간이라고 생각했어요!
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.
아하~ 해당 표기가 비표준이긴 하지만 데이터베이스 쿼리 상에서는 다음 날의 ${date+1}T00:00:00Z와 동일하게 해석된다고 하네요!
src/lib/apis/user.api.ts
Outdated
| * 유저 정보를 변경하는 함수 | ||
| * | ||
| * @param {Partial<UpdateUserDTO>} userInfo 수정할 일부 정보 | ||
| * @returns {Promise<UserDTO>} 변경 이수 수정된 값 |
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.
변경 이수
| export type MealOverviewDTO = Omit<MealDTO, 'mealDetails'>; | ||
| export type CreateMealDTO = Pick<MealDTO, 'foodImages' | 'ateAt' | 'mealType' | 'memo'>; | ||
| export type CreateMealDetailDTO = Omit<MealDetailDTO, 'id' | 'mealId'>; | ||
|
|
||
| export type MealSnakeCaseDTO = SnakeCaseObject<MealDTO>; | ||
| export type MealDetailSnakeCaseDTO = SnakeCaseObject<MealDetailDTO>; | ||
| export type MealOverviewSnakeCaseDTO = SnakeCaseObject<MealOverviewDTO>; |
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.
타입이 깔꼼합니다.. 난 언제쯤..? 🙄
woohyuckk
left a 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.
API 사용을 너무 잘 정리해주셔서 감사합니다!!
체크사항은 코멘트로 남겨놓았습니다.
| const { data, error } = await supabase | ||
| .from('meals') | ||
| .select(` *, meal_details (*) `) | ||
| .gte('ate_at', startDateTime) | ||
| .lte('ate_at', endDateTime) | ||
| .order('ate_at', { ascending: false }); | ||
| if (error) throw error; |
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.
로그인한 유저 기준이 아니라 전체 식사데이터를 불러오고 있는것 같습니다
저희가 전체 기간에 모든 유저의 데이터를 받아오는 경우가 있을까요?
아니라면 .eq(user_id, userId) 조건이 필요할 것 같습니다!
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.
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.
아니 이런 방법이...? 미쳤네요! supabase에서 저런 설정이 가능했군요
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.
오 그럼 유저 프로필도 굳이 id 대조안하고 자기 정보만 가져오게 할 수 있겠당!
| const { data, error } = await supabase | ||
| .from('meals') | ||
| .select(` *, meal_details (*) `) | ||
| .gte('ate_at', `${date}T00:00:00Z`) | ||
| .lt('ate_at', `${date}T24:00:00Z`) | ||
| .order('ate_at', { ascending: false }); | ||
| if (error) throw error; |
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.
여기서도 해당유저의 원하는 날짜의 식사가 아닌
입력된 기간에 있는 모든 meal 데이터를 불러오고 있는 것 같습니다!
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.
위 질문의 답변과 동일합니다!
| const supabase = getServerClient(); | ||
|
|
||
| const file = formData.get('file') as File; | ||
| const filename = crypto.randomUUID() + sanitizeFilename(file.name); |
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.
파일이름을 정제하는 것을 sanitize라고 하는군요!
arendt9797
left a 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.
다은님의 다재다능한 코딩 실력 잘 봤습니다
api 작성하시느라 수고 많으셨습니다!
| * 파일명을 S3 객체 키에서 사용 가능한 안전한 형식으로 변환합니다. | ||
| * | ||
| * 이 함수는 S3에서 허용되지 않는 문자를 제거하거나 안전한 문자로 대체합니다. | ||
| * 특별한 처리가 필요한 문자들도 적절히 처리합니다. |
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.
특별한 처리가 필요한 문자란게 &$@=;:+,?를 말씀하신 것 같습니다.
이 문자들을 특별하게 처리하진 않고 안전한 문자로 같이 처리하신거죠?
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.
아 제가 코드로 참고 링크를 남긴다고 메모도 해두었는데 까먹었습니다!
참고 링크 :
&$@=;:+,? 해당 문자들은 사용가능한 축에 속합니다!
대체 되는 특수 기호는 AWS 참고자료에 가면 더 볼 수 있습니다!
| } as const; | ||
|
|
||
| export type GenderKey = keyof typeof Gender; | ||
| export type GenderValue = (typeof Gender)[GenderKey]['label']; |
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.
혹시 'label'이 아니라 'value'를 의도하신 걸까요?
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.
여기서는 label을 의도한게 맞습니다!
해당값을 value로 잡게 되면 위의 GenderKey와 동일한 타입이 나오게 됩니다.
옴 객체이기 때문에 Key와 Value로 나누는게 좋겠지 하고 이렇게 했는데요,
변수 타입명을 GenderLabel 로 변경하는 편이 좋을까요?
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.
아하 그렇군요! 타입을 사용할 때는 GenderKey와 GenderValue로 나뉘는게 더 좋을 것 같습니다.
변경 안 하셔도 됩니다!
| } as const; | ||
|
|
||
| export type MealTypeKey = keyof typeof MealTypeMap; | ||
| export type MealTypeValue = (typeof MealTypeMap)[MealTypeKey]['label']; |
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.
혹시 'label'이 아니라 'value'를 의도하신 걸까요?
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.
여기서는 label을 의도한게 맞습니다!
해당값을 value로 잡게 되면 위의 GenderKey와 동일한 타입이 나오게 됩니다.
옴 객체이기 때문에 Key와 Value로 나누는게 좋겠지 하고 이렇게 했는데요,
변수 타입명을 GenderLabel 로 변경하는 편이 좋을까요?
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.
위와 마찬가지로 변경 안 하셔도 될 것 같습니다!
| const { data, error } = await supabase | ||
| .from('meals') | ||
| .select(` *, meal_details (*) `) | ||
| .gte('ate_at', startDateTime) | ||
| .lte('ate_at', endDateTime) | ||
| .order('ate_at', { ascending: false }); | ||
| if (error) throw error; |
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.
아니 이런 방법이...? 미쳤네요! supabase에서 저런 설정이 가능했군요

#️⃣ 연관된 이슈
📝 작업 내용
🖼 스크린샷
💬 리뷰 요구사항
40분meal_details테이블을 수정할 수 없게한다고 해서 삭제했습니다.테스트 코드 `src/app/page.tsx`
Summary by CodeRabbit
Summary by CodeRabbit