-
Notifications
You must be signed in to change notification settings - Fork 22
[mission4] 4단계 미션 구현 - 안지선 / 1팀 #42
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
[mission4] 4단계 미션 구현 - 안지선 / 1팀 #42
Conversation
…ssion-2 [mission2] 2단계 미션 구현 - 안지선 / 1팀
[Mission 2] 2단계 미션 구현 - 박진우A / 2팀
han-nun0107
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.
리뷰 확인해 주세요 !
src/App.jsx
Outdated
| const [movies, setMovies] = useState([]); | ||
| const [loading, setLoading] = useState(true); | ||
|
|
||
| // 컴포넌트 마운트 시 API 호출 | ||
| useEffect(() => { | ||
| const fetchMovies = async () => { | ||
| try { | ||
| setLoading(true); | ||
| const data = await getPopularMovies(); | ||
| setMovies(data); | ||
| } catch (error) { | ||
| console.error("영화 데이터 로딩 실패:", error); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| fetchMovies(); | ||
| }, []); | ||
|
|
||
| // 로딩 중일 때 | ||
| if (loading) { | ||
| return <div className="loading">영화 목록을 불러오는 중...</div>; | ||
| } |
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.
커스텀 훅으로 분리하여 관심사 분리 해주는 것이 좋을 것 같아요 !
ex)
export function useMovies() {
const [movies, setMovies] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchMovies = async () => {
try {
setLoading(true);
const data = await getPopularMovies();
setMovies(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchMovies();
}, []);
return { movies, loading, error };
}
src/App.jsx
Outdated
| <div className="app-container"> | ||
| <h1 className="app-title">인기 영화</h1> | ||
| <div className="movie-grid"> | ||
| {movies.map((movie) => ( | ||
| <MovieCard key={movie.id} movie={movie} /> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
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.
seo 고려해서 section, article 사용 고려해보시면 좋을 것 같아요
src/pages/Login.jsx
Outdated
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | ||
| if (!formData.email) { | ||
| newErrors.email = "이메일을 입력해주세요"; | ||
| } else if (!emailRegex.test(formData.email)) { | ||
| newErrors.email = "올바른 이메일 형식을 입력해주세요"; | ||
| } | ||
|
|
||
| // 비밀번호 검증 | ||
| if (!formData.password) { | ||
| newErrors.password = "비밀번호를 입력해주세요"; | ||
| } else if (formData.password.length < 6) { | ||
| newErrors.password = "비밀번호는 6자 이상이어야 합니다"; | ||
| } | ||
|
|
||
| setErrors(newErrors); | ||
| return Object.keys(newErrors).length === 0; | ||
| }; |
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.
유틸로 분리 되면 좋을 것 같아요
ex)
export function validateLoginForm({ email, password }) {
const errors = {};
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email) {
errors.email = "이메일을 입력해주세요";
} else if (!emailRegex.test(email)) {
errors.email = "올바른 이메일 형식을 입력해주세요";
}
if (!password) {
errors.password = "비밀번호를 입력해주세요";
} else if (password.length < 6) {
errors.password = "비밀번호는 6자 이상이어야 합니다";
}
return errors;
}
src/pages/Login.jsx
Outdated
| const handleChange = (e) => { | ||
| const { name, value } = e.target; | ||
| setFormData((prev) => ({ | ||
| ...prev, | ||
| [name]: value, | ||
| })); | ||
| // 입력 시 에러 제거 | ||
| if (errors[name]) { | ||
| setErrors((prev) => ({ | ||
| ...prev, | ||
| [name]: "", | ||
| })); | ||
| } | ||
| }; | ||
|
|
||
| const handleSubmit = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| if (!validateForm()) return; | ||
|
|
||
| const result = await login(formData.email, formData.password); | ||
|
|
||
| if (result.success) { | ||
| navigate("/"); | ||
| } | ||
| }; |
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.
관심사 분리 해주면 좋을 것 같아요 ! 커스텀 훅으로
import { useState } from "react";
import { validateLoginForm } from "@/utils/validationUtils";
export function useLoginForm() {
const [formData, setFormData] = useState({ email: "", password: "" });
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
setErrors((prev) => ({ ...prev, [name]: "" }));
};
const handleSubmit = async (onValid) => {
const validationErrors = validateLoginForm(formData);
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
await onValid(formData);
}
};
return { formData, errors, handleChange, handleSubmit };
}
src/pages/SignUp.jsx
Outdated
| const navigate = useNavigate(); | ||
| const { signup, loading, error: authError } = useSupabaseAuth(); | ||
| const [formData, setFormData] = useState({ | ||
| name: "", | ||
| email: "", | ||
| password: "", | ||
| confirmPassword: "", | ||
| }); | ||
| const [errors, setErrors] = useState({}); | ||
|
|
||
| const handleChange = (e) => { | ||
| const { name, value } = e.target; | ||
| setFormData((prev) => ({ ...prev, [name]: value })); | ||
| }; | ||
|
|
||
| const handleSubmit = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // 유효성 검사 | ||
| const newErrors = { | ||
| name: validateName(formData.name), | ||
| email: validateEmail(formData.email), | ||
| password: validatePassword(formData.password), | ||
| confirmPassword: validatePasswordMatch( | ||
| formData.password, | ||
| formData.confirmPassword | ||
| ), | ||
| }; | ||
|
|
||
| setErrors(newErrors); | ||
|
|
||
| // 에러가 있으면 진행하지 않기 | ||
| if (Object.values(newErrors).some((err) => err)) { | ||
| return; | ||
| } | ||
|
|
||
| // 회원가입 처리 | ||
| const result = await signup( | ||
| formData.email, | ||
| formData.password, | ||
| formData.name | ||
| ); | ||
| if (result.success) { | ||
| alert("회원가입 완료! 로그인 페이지로 이동합니다."); | ||
| navigate("/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.
요 부분도 커스텀 훅으로 관심사 분리 해주시면 좋을 것 같습니다 !!
src/pages/Login.jsx
Outdated
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | ||
| if (!formData.email) { | ||
| newErrors.email = "이메일을 입력해주세요"; | ||
| } else if (!emailRegex.test(formData.email)) { | ||
| newErrors.email = "올바른 이메일 형식을 입력해주세요"; | ||
| } | ||
|
|
||
| // 비밀번호 검증 | ||
| if (!formData.password) { | ||
| newErrors.password = "비밀번호를 입력해주세요"; | ||
| } else if (formData.password.length < 6) { | ||
| newErrors.password = "비밀번호는 6자 이상이어야 합니다"; | ||
| } |
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.
src/utils/validation.js에 있는 것으로 대체 될 것 같은데 확인 한 번 부탁드려요 !!!
vite.config.js
Outdated
| }, | ||
| optimizeDeps: { | ||
| force: true, // 의존성 강제 재최적화 | ||
| }, |
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.
절대 경로 설정해서 import 절대 경로로 수정되면 좋을 것 같아요 !
vite 절대경로 설정이라고 검색하면 자료 많이 나옵니다 !!!
23ab5b5 to
7108d65
Compare
[과제4] 컴포넌트별 CSS 분리 및 UI 코드 구조 개선
#!/usr/bin/env sh,$(dirname -- "$0")/_/husky.sh") 삭제