Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .idea/caches/deviceStreaming.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion src/components/modals/DeleteAccountModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import {
View,
Text,
Expand All @@ -16,6 +16,8 @@ import {
import { Ionicons as Icon } from "@expo/vector-icons";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { authAPI } from "../../services";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from "../../services/apiConfig";

interface DeleteAccountModalProps {
isOpen: boolean;
Expand All @@ -35,6 +37,7 @@ const DeleteAccountModal: React.FC<DeleteAccountModalProps> = ({
const [showPassword, setShowPassword] = useState(false);

const validateInputs = () => {
// 일반 사용자는 비밀번호와 탈퇴 사유 필수
if (!password.trim()) {
Alert.alert("오류", "비밀번호를 입력해주세요.");
return false;
Expand All @@ -49,6 +52,7 @@ const DeleteAccountModal: React.FC<DeleteAccountModalProps> = ({
};

const handleDeleteAccount = async () => {
// 입력 검증
if (!validateInputs()) {
return;
}
Expand All @@ -68,6 +72,8 @@ const DeleteAccountModal: React.FC<DeleteAccountModalProps> = ({
onPress: async () => {
try {
setLoading(true);

// 일반 사용자 회원 탈퇴
const response = await authAPI.deleteAccount(password, reason);

if (response.success) {

Choose a reason for hiding this comment

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

코드 패치를 검토한 결과, 몇 가지 잠재적인 문제와 개선 사항을 발견했습니다.

  1. 비동기 호출의 오류 처리: handleDeleteAccount 함수 내의 authAPI.deleteAccount 호출에서 오류가 발생할 경우 이를 처리하는 로직이 없습니다. try-catch 블록을 추가하여 서버 오류나 네트워크 문제를 처리해야 합니다.

  2. 비밀번호 확인: 비밀번호와 탈퇴 사유를 입력받는 부분에서 추가적인 검증이 필요할 수 있습니다. 예를 들어, 비밀번호가 특정 길이 이상인지 확인하는 등의 추가적인 룰을 고려할 수 있습니다.

  3. 상태 관리: 사용자의 입력 상태를 관리하는 부분에서 리액트의 상태 변경 및 API 호출 후의 상태 업데이트도 마찬가지로 고려해야 합니다. 예를 들어, setLoading(false) 호출이 누락되면 로딩 상태가 유지될 수 있습니다.

  4. 상수 파일 구조 개선: ACCESS_TOKEN_KEYREFRESH_TOKEN_KEY 상수들이 어디에서 사용될지 불명확합니다. 이러한 상수들은 보안과 관련된 센티티이므로, 적절한 위치에 배치하고 코드 베이스에서의 사용을 제한할 수 있어야 합니다.

  5. AsyncStorage 사용의 의도: 코드에서 AsyncStorage가 임포트 되긴 했으나, 실제로 사용되고 있지 않습니다. 사용하지 않는다면 임포트를 제거해야 하며, 사용 계획이 있다면 이를 코드에 명확하게 반영해야 합니다.

위의 문제들을 해결한 뒤에 코드 병합을 고려하는 것이 좋습니다.

Expand Down
2 changes: 1 addition & 1 deletion src/navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type RootStackParamList = {
InBodyManual: undefined;
HealthScoreTrend: undefined;
// Payment
PaymentSuccess: { sessionId?: string; orderId?: string };
PaymentSuccess: undefined;
PaymentFail: undefined;
PaymentCancel: undefined;
};

Choose a reason for hiding this comment

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

리뷰 코멘트:

  1. 변경 사항: PaymentSuccess 타입을 { sessionId?: string; orderId?: string }에서 undefined로 변경했습니다.

  2. 문제점: 이 변경은 PaymentSuccess 경로를 사용하는 컴포넌트나 함수에서 sessionIdorderId를 더 이상 사용하지 않도록 강제합니다. 만약 코드의 다른 부분에서 이 두 개의 파라미터가 필요하다면, 프로그램이 예상치 못한 동작을 할 수 있습니다.

  3. 위험 요소: 이 변경이 기존 코드와의 호환성에 영향을 미칠 수 있습니다. 만약 다른 코드 조각이 PaymentSuccess를 호출할 때 세션 ID와 주문 ID를 필요로 한다면, 이는 런타임 에러를 초래할 수 있습니다.

  4. 제안 사항: 의도한 변경이 맞다면, 이를 뒷받침하는 주석을 추가하여 더 명확하게 해주세요. 또한, 코드의 다른 부분에서 PaymentSuccess 경로를 사용하는 모든 위치를 점검하여 이 변경으로 인해 발생할 수 있는 모든 잠재적 문제를 식별하고 수정하는 것을 권장합니다.

Expand Down
67 changes: 43 additions & 24 deletions src/screens/auth/KakaoOnboardingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ const healthGoalOptions = [
];

const workoutDaysOptions = [
{label: '주 1회', value: '주 1회'},
{label: '주 2회', value: '주 2회'},
{label: '주 3회', value: '주 3회'},
{label: '주 4회', value: '주 4회'},
{label: '주 5회', value: '주 5회'},
{label: '주 6회', value: '주 6회'},
{label: '주 7회', value: '주 7회'},
{label: '주 1회', value: '1일'},
{label: '주 2회', value: '2일'},
{label: '주 3회', value: '3일'},
{label: '주 3-4회', value: '3-4일'},
{label: '주 4회', value: '4일'},
{label: '주 5회', value: '5일'},
{label: '주 6회', value: '6일'},
{label: '주 7회', value: '7일'},
];

const experienceLevelOptions = [
Expand Down Expand Up @@ -144,35 +145,53 @@ const KakaoOnboardingScreen = ({navigation}: any) => {
try {
const birthDate = `${formData.birthYear}-${String(formData.birthMonth).padStart(2, '0')}-${String(formData.birthDay).padStart(2, '0')}`;

// 유연성향상, 체력증진, 자세교정은 "MAINTENANCE"로 변환
const healthGoalValue = ['FLEXIBILITY', 'ENDURANCE', 'POSTURE'].includes(formData.healthGoal)
? 'MAINTENANCE'
: formData.healthGoal;

const onboardingData = {
gender: formData.gender,
birthDate,
agreePrivacy: true,
agreeTerms: true,
gender: formData.gender as "M" | "F",
height: Number(formData.height),
weight: Number(formData.weight),
birthDate,
weightGoal: Number(formData.weightGoal),
healthGoal: formData.healthGoal,
healthGoal: healthGoalValue,
workoutDaysPerWeek: formData.workoutDaysPerWeek,
...(formData.experienceLevel && {experienceLevel: formData.experienceLevel}),
...(formData.fitnessConcerns && {fitnessConcerns: formData.fitnessConcerns}),
};

const response = await authAPI.submitOnboarding(onboardingData);

if (response.success) {
Alert.alert('완료', '신체정보가 저장되었습니다.', [
{
text: '확인',
onPress: () => navigation.replace('Main'),
},
]);
} else {
Alert.alert('오류', response.message || '신체정보 저장에 실패했습니다.');
}
setLoading(false);

// 200 응답 (온보딩 완료) → Alert 없이 바로 홈으로 이동
// response.success가 false여도 200 응답이면 성공으로 처리
setTimeout(() => {
navigation.replace('Main');
}, 100);
return;
} catch (error: any) {
console.error('온보딩 제출 실패:', error);
Alert.alert('오류', error.message || '신체정보 저장에 실패했습니다.');
} finally {
setLoading(false);

// 400 에러 (이미 온보딩 완료됨) → Alert 없이 홈으로 이동
if (error.status === 400) {
setTimeout(() => {
navigation.replace('Main');
}, 100);
return;
}

// 401 에러 (인증 필요) → 로그인 화면으로 이동
if (error.status === 401) {
navigation.replace('Login');
return;
}

// 기타 에러만 Alert 표시
Alert.alert('오류', error.message || '신체정보 저장에 실패했습니다.');
}
};

Choose a reason for hiding this comment

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

피드백

  1. 버그 관련: 코드에서 응답이 200인 경우에 대해 성공으로 처리하고 있습니다. 그러나, 실제로 response.success가 false일 때 시스템이 기대한 대로 작동하지 않을 수 있습니다. 이는 사용자가 온보딩을 완료했으나 서버가 이상적으로 반응할 때를 고려하지 않습니다.

  2. 반복 코드: navigation.replace('Main')와 같은 코드가 여러 번 반복되고 있습니다. 이 경우 함수로 추출하여 DRY(Don't Repeat Yourself) 원칙을 적용하는 것이 좋습니다.

  3. 에러 처리: 400 및 401 에러를 처리하는 로직이 추가되었습니다. 그러나, 그 외의 상태 코드에 대한 처리도 중요합니다. 예를 들어, 500 서버 에러 같은 경우에는 사용자에게 적절한 안내를 제공해야 합니다.

  4. 부정확한 상태 처리: error.status가 정의되지를 않을 수 있습니다. API 호출 중에 발생한 예외에 대한 일관된 형식의 오류 처리가 이루어지지 않을 수 있습니다. try-catch 블록에서 서버 응답의 형식을 명확히 확인하고, 이에 따라 적절한 예외 처리를 해야 합니다.

  5. 성능: setTimeout을 사용하여 지연 후 화면을 전환하는 대신, API 요청의 실제 응답에 따라 바로 전환이 이루어지도록 코드를 개선하는 것이 좋습니다. 이는 사용자 경험 향상에 도움이 될 것입니다.

  6. 주석: 주석은 코드의 의미를 명확히 설명하는 데 도움이 됩니다. 그러나, 주석이 반복적으로 설명하는 경우에는 코드가 충분히 명확하게 보이도록 개선해야 합니다.

Expand Down
Loading