Skip to content

Conversation

@anneedesign-ui
Copy link
Contributor

[과제4] 컴포넌트별 CSS 분리 및 UI 코드 구조 개선

  • 모든 컴포넌트 CSS 파일을 각각 분리/정리하여 유지보수성 강화
  • styles 폴더 내 중복 CSS 삭제 및 import 경로 통일
  • husky pre-commit hook 관련 피드백 반영:
    • deprecated 코드(#!/usr/bin/env sh, $(dirname -- "$0")/_/husky.sh") 삭제
    • 최신 husky hook 구조(npx lint-staged)로 수정
    • 커밋 전 자동 포맷 및 코드 스타일 검증 적용
  • PR 리뷰에서 받은 코드 구조/작명 피드백까지 적극 반영
  • MovieCard, NavBar 등 UI 일관성 및 스타일링도 실무 경험 기반으로 개선

fortes42-lgtm and others added 6 commits October 28, 2025 11:47
…ssion-2

[mission2] 2단계 미션 구현 - 안지선 / 1팀
…ature/mission-2"

This reverts commit 190af1c, reversing
changes made to cae66dc.
[Mission 2] 2단계 미션 구현 - 박진우A / 2팀
…sion-2"

This reverts commit b185bf8, reversing
changes made to 3cb8835.
Copy link
Collaborator

@han-nun0107 han-nun0107 left a 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
Comment on lines 7 to 30
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>;
}
Copy link
Collaborator

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
Comment on lines 33 to 42
<div className="app-container">
<h1 className="app-title">인기 영화</h1>
<div className="movie-grid">
{movies.map((movie) => (
<MovieCard key={movie.id} movie={movie} />
))}
</div>
</div>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

seo 고려해서 section, article 사용 고려해보시면 좋을 것 같아요

Comment on lines 22 to 38
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;
};
Copy link
Collaborator

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;
}

Comment on lines 40 to 65
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("/");
}
};
Copy link
Collaborator

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 };
}

Comment on lines 14 to 60
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");
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

요 부분도 커스텀 훅으로 관심사 분리 해주시면 좋을 것 같습니다 !!

Comment on lines 22 to 34
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자 이상이어야 합니다";
}
Copy link
Collaborator

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, // 의존성 강제 재최적화
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

절대 경로 설정해서 import 절대 경로로 수정되면 좋을 것 같아요 !
vite 절대경로 설정이라고 검색하면 자료 많이 나옵니다 !!!

@anneedesign-ui anneedesign-ui deleted the feature/mission-4 branch November 18, 2025 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants