Skip to content

20251121 #34 로그인 페이지 UI 개발#39

Merged
Chuseok22 merged 14 commits intotestfrom
20251121_#34_로그인_페이지_UI_개발
Nov 21, 2025

Hidden character warning

The head ref may contain hidden characters: "20251121_#34_\ub85c\uadf8\uc778_\ud398\uc774\uc9c0_UI_\uac1c\ubc1c"
Merged

20251121 #34 로그인 페이지 UI 개발#39
Chuseok22 merged 14 commits intotestfrom
20251121_#34_로그인_페이지_UI_개발

Conversation

@Chuseok22
Copy link
Copy Markdown
Member

@Chuseok22 Chuseok22 commented Nov 21, 2025

✨ 변경 사항


✅ 테스트


  • 수동 테스트 완료
  • 테스트 코드 완료

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 로그인 페이지 추가: 사용자명과 비밀번호 입력 폼 제공
    • 클라이언트 측 폼 검증 기능 추가
    • 로그인 시도 중 로딩 상태 표시
    • 유효하지 않은 입력에 대한 오류 메시지 및 시각적 피드백 제공
  • 스타일

    • 인증 페이지 레이아웃 개선
    • 오류 상태에서 입력 필드의 빨간색 밑줄 및 애니메이션 추가
    • 페이지 여백 및 간격 조정

✏️ Tip: You can customize this high-level summary in your review settings.

@Chuseok22 Chuseok22 self-assigned this Nov 21, 2025
@Chuseok22 Chuseok22 added the feat 새로운 기능 추가 (Feature) label Nov 21, 2025
@Chuseok22 Chuseok22 linked an issue Nov 21, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 21, 2025

Walkthrough

로그인 페이지 UI를 구현하는 기능 추가로, LoginPage 컴포넌트, 폼 검증 및 오류 상태 처리, 입력 필드 오류 표시, 로딩 상태 관리, 전용 레이아웃 컴포넌트(AuthPageView), 오류 애니메이션 및 관련 스타일을 도입합니다.

Changes

Cohort / File(s) 요약
로그인 페이지 핵심 구현
src/app/(auth)/login/page.tsx, src/app/(auth)/login/page.module.css
LoginPage 컴포넌트 신규 생성 및 CSS 모듈 추가, 로고 플레이스홀더 및 LoginForm 통합 렌더링
인증 레이아웃 전환
src/app/(auth)/layout.tsx, src/shared/components/layout/page-frame/view/AuthPageView.tsx, src/shared/components/layout/page-frame/view/auth-page-view.module.css
PageView에서 AuthPageView로 변경, 신규 AuthPageView 컴포넌트 및 스타일 추가
로그인 폼 상태 및 검증
src/features/auth/components/form/LoginForm.tsx, src/features/auth/components/form/login-form.module.css, src/features/auth/utils/form/loginFormUtils.ts
폼 검증(isValidSubmit), 비활성화 로직(isDisabled), 오류/로딩 상태 추가, 레이아웃 컨테이너 CSS 추가
입력 필드 오류 표시
src/features/auth/components/input/UsernameInput.tsx, src/features/auth/components/input/PasswordInput.tsx, src/features/auth/components/input/username-input.module.css, src/features/auth/components/input/password-input.module.css
선택적 hasError props 추가, 오류 상태 시각화(빨간색 언더라인 + shake 애니메이션)
오류 레이블 컴포넌트
src/features/auth/components/label/LoginFormErrorLabel.tsx, src/features/auth/components/label/login-form-error-label.module.css
신규 오류 메시지 레이블 컴포넌트, ErrorCircleFilled 아이콘 포함
스타일 및 아이콘 확장
src/app/globals.css, src/assets/icons/index.ts, src/styles/base/animations.css, src/styles/base/layouts.css, src/shared/components/layout/page-frame/view/page-view.module.css, src/shared/components/navigation/bottom-navigation.module.css
애니메이션 스타일 임포트, ErrorCircleFilled 아이콘 재내보내기, error-shake 키프레임 정의, 레이아웃 높이 변수 추가, 기존 PageView 및 하단 네비게이션 스타일 조정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LoginPage as LoginPage
    participant LoginForm as LoginForm
    participant Utils as Validators
    participant UI as Input/Button/Label

    User->>LoginPage: 페이지 방문
    LoginPage->>LoginForm: 마운트
    
    User->>UI: 사용자명 입력
    LoginForm->>Utils: trim & 검증
    Utils->>LoginForm: 오류 초기화
    LoginForm->>UI: hasError = false
    
    User->>UI: 비밀번호 입력
    LoginForm->>Utils: trim & 검증
    Utils->>LoginForm: 오류 초기화
    LoginForm->>UI: hasError = false
    
    User->>UI: 제출 버튼 클릭
    LoginForm->>Utils: isValidSubmit() 호출
    
    alt 검증 실패
        Utils-->>LoginForm: false
        LoginForm->>LoginForm: error = true
        LoginForm->>UI: LoginFormErrorLabel 표시
        UI->>UI: 언더라인 + shake 애니메이션
    else 검증 성공
        Utils-->>LoginForm: true
        LoginForm->>LoginForm: error = false, loading = true
        LoginForm->>UI: 버튼 disabled, 로딩 표시
        LoginForm->>LoginForm: 3초 후 loading = false
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의 깊게 검토 필요 영역:
    • src/features/auth/utils/form/loginFormUtils.ts: 하드코딩된 오류 사용자명 "22011315" 검증 로직 확인 (의도적인 테스트 로직인지 확인 필요)
    • src/features/auth/components/form/LoginForm.tsx: 상태 관리, 폼 검증 흐름, 3초 로딩 시뮬레이션 로직의 정확성
    • src/shared/components/layout/page-frame/view/page-view.module.css: justify-content 변경(center → flex-start)의 다른 페이지 영향 범위 확인
    • CSS 모듈들: 오류 애니메이션 및 색상 변수(--color-red, --animation-error-shake-once) 존재 여부 확인

Possibly related issues

Possibly related PRs

Poem

🐰 로그인 폼을 짰다네, 입력 필드 shake shake shake!
빨간 언더라인 춤을 추고, 오류 메시지 나타났네.
검증, 로딩, 오류 상태, 모두 담았다네.
AuthPageView로 감싸서 멋지게 완성!
사용자들이 로그인할 준비됐어! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 변경 사항의 주요 목적을 명확하게 설명합니다. 로그인 페이지 UI 개발이라는 실질적 변화를 직접적으로 반영하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20251121_#34_로그인_페이지_UI_개발

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (9)
src/styles/base/layouts.css (1)

22-24: 레이아웃 토큰 분리 정의는 괜찮지만, 하나의 @theme 블록으로 정리하는 것도 고려해볼 만합니다.

--top-header-height, --bottom-navigation-height 토큰 추가로 레이아웃 일관성 확보에는 도움이 됩니다. 다만 spacing 토큰과 같은 레벨의 전역 레이아웃 토큰이라면, 동일한 @theme 블록 안에 함께 두면 추후 토큰 관리/검색이 조금 더 수월할 수 있습니다. 현재 상태도 동작에는 문제 없습니다.

src/features/auth/components/form/login-form.module.css (1)

7-23: 고정된 margin-top: 14.75rem는 작은 화면에서 레이아웃 깨질 수 있습니다.

현재 구조상 버튼 영역을 아래로 내리기 위해 큰 margin-top을 사용하고 있는데, 모바일 기기나 짧은 뷰포트에서 버튼이 화면 밖으로 밀릴 수 있습니다.

가능하다면 다음과 같은 방향을 검토해보면 좋겠습니다.

  • 부모 컨테이너에서 display: flex; flex-direction: column; justify-content: space-between; 등을 사용해 자연스럽게 위/아래를 분리하거나
  • 버튼 컨테이너에 margin-top: auto;를 주는 방식으로, 뷰포트 높이에 따라 유동적으로 배치되도록 조정

디자인 시안이 고정 오프셋을 요구하지 않는다면, 유연한 레이아웃으로 바꾸는 것을 추천합니다.

src/features/auth/components/input/password-input.module.css (1)

61-68: 에러 상태 언더라인/애니메이션 처리 방향 좋습니다.

containerError에서 언더라인 높이와 색을 빨간색으로 고정하고 var(--animation-error-shake-once)를 적용해, 에러 시 시각적 피드백이 명확해졌습니다.

작은 제안 하나만 드리면, 높이 변화 애니메이션도 사용하고 싶다면 transitionvar(--transition-height-fast)를 함께 두어 포커스/에러 간 전환 느낌을 맞춰주는 것도 고려해볼 수 있습니다.

src/app/(auth)/login/page.tsx (1)

1-12: LoginPage 구조는 단순명료하며, LoginForm를 감싸는 역할에 잘 맞습니다.

두 가지 정도만 확인/제안 드립니다.

  1. LoginForm Client Component 여부 확인 필요
    LoginForm에서 useState 등 React 훅을 사용하고 있어, Next App Router 기준으로는 해당 파일 상단에 "use client" 선언이 있어야 합니다. 스니펫 상단이 잘리지 않았다면, 한 번 더 확인 부탁드립니다. 이 페이지 컴포넌트 자체는 Server Component여도, 자식으로 Client Component를 두는 패턴은 문제 없습니다.

  2. 메인 카피를 시맨틱 heading으로 감싸는 것 고려
    "먹고 싶은 학식, 바로 주문해요!"가 로그인 페이지의 대표 문구라면, <div> 대신 <h1> 등 heading 태그로 감싸 두면 접근성/SEO 측면에서 이점이 있습니다. 예시:

-      <div className={styles.label}>먹고 싶은 학식, 바로 주문해요!</div>
+      <h1 className={styles.label}>먹고 싶은 학식, 바로 주문해요!</h1>

(이미 상위 레이아웃에 다른 h1이 있다면 그 구조를 우선 고려해 주세요.)

src/shared/components/layout/page-frame/view/auth-page-view.module.css (1)

1-13: 인증 페이지 전용 레이아웃이 추가되었습니다.

page-view.module.css와 매우 유사한 구조이지만 safe-area 변수를 사용하는 점이 다릅니다. 두 레이아웃의 중복을 줄이기 위해 공통 베이스 스타일과 변형(variant)을 고려할 수 있지만, 인증 페이지와 일반 페이지의 의미적 분리를 위해 현재 구조가 적절할 수 있습니다.

만약 통합을 고려한다면 다음과 같은 접근을 생각해볼 수 있습니다:

/* 공통 베이스 */
.containerBase {
  background-color: var(--color-white);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: stretch;
  max-width: var(--container-3xl);
  min-height: 100vh;
  padding-inline: var(--spacing-gutter);
  width: 100%;
}

/* 변형: 인증 페이지 */
.containerAuth {
  padding-top: var(--safe-top);
  padding-bottom: var(--safe-bottom);
}

/* 변형: 일반 페이지 */
.containerDefault {
  padding-top: var(--top-header-height);
  padding-bottom: var(--bottom-navigation-height);
}
src/features/auth/components/input/UsernameInput.tsx (1)

20-22: className 조건부 로직이 명확합니다.

에러 상태에 따라 적절한 클래스를 적용하고 있습니다. PasswordInput.tsx에서도 동일한 패턴이 사용되고 있으므로, 선택적으로 공통 유틸리티 함수로 추출하여 중복을 줄일 수 있습니다.

선택적 리팩토링 예시:

// src/features/auth/utils/input/getInputContainerClassName.ts
export function getInputContainerClassName(
  styles: Record<string, string>,
  hasError: boolean
): string {
  return hasError
    ? `${styles.container} ${styles.containerError}`
    : styles.container;
}

사용:

const containerClassName = getInputContainerClassName(styles, hasError);

Also applies to: 25-25

src/features/auth/components/input/PasswordInput.tsx (1)

25-27: 에러 상태 className 로직이 UsernameInput과 동일합니다.

두 입력 컴포넌트에서 동일한 패턴을 사용하고 있어 일관성이 유지됩니다. UsernameInput 리뷰에서 언급한 것처럼, 선택적으로 공통 유틸리티 함수로 추출하여 중복을 줄일 수 있습니다.

Also applies to: 37-37

src/shared/components/layout/page-frame/view/AuthPageView.tsx (1)

1-1: 선택 사항: React 19에서는 React import가 불필요합니다.

React 19에서는 새로운 JSX transform이 적용되어 JSX 사용 시 React를 명시적으로 import할 필요가 없습니다. 이 import는 제거해도 됩니다.

다음 diff를 적용하여 불필요한 import를 제거하세요:

-import React from "react";
 import styles from "./auth-page-view.module.css";
src/features/auth/components/form/LoginForm.tsx (1)

44-47: TODO 주석: API 연동을 추적해야 합니다.

로그인 API 연동이 필요하다는 TODO 주석이 있습니다. 현재는 3초 딜레이로 시뮬레이션되고 있습니다.

이 TODO 항목을 추적하기 위한 별도 이슈를 생성하시겠습니까? 또는 API 연동 코드 작성을 도와드릴 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d01320e and ee15df3.

⛔ Files ignored due to path filters (1)
  • src/assets/icons/error-circle-filled.svg is excluded by !**/*.svg
📒 Files selected for processing (20)
  • src/app/(auth)/layout.tsx (2 hunks)
  • src/app/(auth)/login/page.module.css (1 hunks)
  • src/app/(auth)/login/page.tsx (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/assets/icons/index.ts (1 hunks)
  • src/features/auth/components/form/LoginForm.tsx (1 hunks)
  • src/features/auth/components/form/login-form.module.css (1 hunks)
  • src/features/auth/components/input/PasswordInput.tsx (2 hunks)
  • src/features/auth/components/input/UsernameInput.tsx (1 hunks)
  • src/features/auth/components/input/password-input.module.css (1 hunks)
  • src/features/auth/components/input/username-input.module.css (1 hunks)
  • src/features/auth/components/label/LoginFormErrorLabel.tsx (1 hunks)
  • src/features/auth/components/label/login-form-error-label.module.css (1 hunks)
  • src/features/auth/utils/form/loginFormUtils.ts (1 hunks)
  • src/shared/components/layout/page-frame/view/AuthPageView.tsx (1 hunks)
  • src/shared/components/layout/page-frame/view/auth-page-view.module.css (1 hunks)
  • src/shared/components/layout/page-frame/view/page-view.module.css (1 hunks)
  • src/shared/components/navigation/bottom-navigation.module.css (1 hunks)
  • src/styles/base/animations.css (1 hunks)
  • src/styles/base/layouts.css (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/app/(auth)/login/page.tsx (1)
src/features/auth/components/form/LoginForm.tsx (1)
  • LoginForm (11-80)
src/app/(auth)/layout.tsx (1)
src/shared/components/layout/page-frame/view/AuthPageView.tsx (1)
  • AuthPageView (4-14)
src/features/auth/components/form/LoginForm.tsx (5)
src/features/auth/utils/form/loginFormUtils.ts (2)
  • isValidSubmit (1-8)
  • isDisabled (10-12)
src/features/auth/components/input/UsernameInput.tsx (1)
  • UsernameInput (13-46)
src/features/auth/components/input/PasswordInput.tsx (1)
  • PasswordInput (14-62)
src/features/auth/components/label/LoginFormErrorLabel.tsx (1)
  • LoginFormErrorLabel (8-22)
src/features/auth/components/button/LoginButton.tsx (1)
  • LoginButton (10-25)
🔇 Additional comments (19)
src/shared/components/navigation/bottom-navigation.module.css (1)

7-12: 바텀 네비 높이 및 안전 영역 패딩 토큰화 방향 좋습니다.

var(--bottom-navigation-height)var(--safe-bottom)을 사용해 높이/패딩을 정의한 것은 디자인 토큰과 safe-area 전략과 잘 맞습니다. 레이아웃 변경 시에도 한 곳에서 조정 가능해 유지보수성이 좋아질 것 같습니다.

src/assets/icons/index.ts (1)

1-1: 새 에러 아이콘 export 추가는 패턴과 일관적입니다.

기존 아이콘들과 동일한 방식으로 ErrorCircleFilled를 export 하고 있어 사용성/일관성 측면에서 괜찮습니다. error-circle-filled.svg 파일 경로와 파일명만 한 번 더 확인해 두면 충분할 것 같습니다.

src/app/(auth)/layout.tsx (1)

2-13: Auth 전용 뷰 컴포넌트로 분리한 구조가 명확합니다.

PageContainer 아래에 AuthPageView를 두어 인증 영역만 별도의 레이아웃/스타일로 관리하는 구조가 깔끔합니다. Auth 관련 다른 페이지에서도 동일 뷰를 재사용하기 좋을 것 같습니다.

src/app/globals.css (1)

6-6: 애니메이션 전역 import 위치가 적절합니다.

colors → layouts → typography → motion 뒤에 animations.css를 가져오는 순서라, 토큰/키프레임 정의가 전역에서 안정적으로 사용 가능해 보입니다. 로그인 폼 에러 애니메이션과 잘 연동될 것 같습니다.

src/app/(auth)/login/page.module.css (3)

1-5: 컨테이너 레이아웃이 적절합니다.

플렉스 컬럼 레이아웃과 중앙 정렬이 로그인 페이지에 적합합니다.


7-15: 로고 플레이스홀더 구현 확인이 필요합니다.

현재 회색 배경의 플레이스홀더로 구현되어 있습니다. 실제 로고 이미지 또는 브랜드 컴포넌트로 교체할 계획인지 확인해 주세요.


17-26: 레이블 스타일링이 디자인 시스템을 잘 따르고 있습니다.

모든 타이포그래피 속성이 CSS 변수를 적절히 사용하고 있습니다.

src/styles/base/animations.css (2)

3-5: 재사용 가능한 애니메이션 변수 정의가 우수합니다.

CSS 커스텀 프로퍼티를 활용하여 에러 애니메이션을 전역적으로 재사용할 수 있도록 잘 구성되어 있습니다.


7-23: 에러 쉐이크 애니메이션이 적절하게 구현되었습니다.

진폭이 점차 감소하는 수평 흔들림 효과가 에러 피드백에 적합하며, 0.25초의 짧은 지속시간으로 사용자 경험을 방해하지 않습니다.

src/features/auth/components/input/username-input.module.css (1)

59-66: 에러 상태 스타일링이 명확하고 효과적입니다.

빨간색 언더라인과 쉐이크 애니메이션을 결합하여 사용자에게 명확한 에러 피드백을 제공합니다. 포커스 상태와 동일한 높이(0.125rem)를 사용하여 일관성을 유지하는 것도 좋습니다.

src/features/auth/components/input/UsernameInput.tsx (1)

10-10: 에러 상태 prop 추가가 적절합니다.

선택적 hasError prop에 기본값 false를 제공하여 기존 코드와의 호환성을 유지하고 있습니다.

Also applies to: 17-17

src/features/auth/components/label/login-form-error-label.module.css (3)

1-6: 에러 레이블 컨테이너 레이아웃이 적절합니다.

플렉스 레이아웃과 일관된 간격 계산으로 깔끔하게 구성되어 있습니다.


8-10: visibility: hidden 사용이 우수한 선택입니다.

display: none 대신 visibility: hidden을 사용하여 에러 메시지가 나타나거나 사라질 때 레이아웃 이동을 방지합니다. 이는 사용자 경험을 개선하는 좋은 패턴입니다.


12-18: 에러 레이블 타이포그래피가 적절합니다.

작은 폰트 크기와 빨간색을 사용하여 에러 메시지를 명확히 전달하면서도 UI를 압도하지 않습니다.

src/features/auth/components/input/PasswordInput.tsx (1)

11-11: 에러 상태 prop 추가가 UsernameInput과 일관됩니다.

동일한 패턴으로 hasError prop을 구현하여 두 입력 컴포넌트 간 일관성을 유지하고 있습니다.

Also applies to: 18-18

src/shared/components/layout/page-frame/view/page-view.module.css (1)

5-10: CSS 변수 검증 완료 - 코드 승인

--top-header-height--bottom-navigation-height 변수가 src/styles/base/layouts.css의 23-24번 줄에서 정확하게 정의되어 있습니다. 헤더 및 네비게이션 높이가 일관되게 적용되고 있으며, 레이아웃 변경이 적절하게 구현되었습니다.

src/features/auth/components/label/LoginFormErrorLabel.tsx (1)

8-22: LGTM!

에러 라벨 컴포넌트가 잘 구현되어 있습니다. visible prop을 통한 조건부 렌더링이 적절하게 처리되었습니다.

src/features/auth/components/form/LoginForm.tsx (2)

18-33: LGTM!

입력 필드 변경 핸들러가 잘 구현되어 있습니다. 사용자가 입력할 때 에러 상태를 적절히 초기화하는 로직이 좋습니다.


50-77: LGTM!

폼 구조와 컴포넌트 통합이 잘 되어 있습니다. disabled 및 hasError props가 적절하게 전달되고 있습니다.

Comment thread src/features/auth/components/form/LoginForm.tsx
Comment thread src/features/auth/utils/form/loginFormUtils.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 새로운 기능 추가 (Feature)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

⚙️ [기능추가][페이지] 로그인 페이지 UI 개발

1 participant