diff --git a/CLAUDE.md b/CLAUDE.md
index 246c94a..2202d41 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -12,12 +12,12 @@ StartPage (활성 스프린트 여부 분기)
└─ NoticePage (안내)
└─ SprintCodePage (인증 코드 입력)
└─ SprintIntroPage (스프린트 소개)
- └─ UserInfoPage (사용자 정보 입력, step 1/7)
- └─ StopCommentPage (step 2/7)
- └─ StartCommentPage (step 3/7)
- └─ ContinueCommentPage (step 4/7)
- └─ MvpPage (step 6/7)
- └─ ClosingPage (step 7/7)
+ └─ UserInfoPage (사용자 정보 입력, step 1/6)
+ └─ StopCommentPage (step 2/6)
+ └─ StartCommentPage (step 3/6)
+ └─ ContinueCommentPage (step 4/6)
+ └─ MvpPage (step 5/6)
+ └─ ClosingPage (step 6/6)
```
---
@@ -85,7 +85,7 @@ src/
│ ├── common/ → 도메인 무관한 범용 컴포넌트
│ │ ├── layout/ (PageLayout, StepLayout)
│ │ ├── form/ (SelectField, InputField)
-│ │ └── ui/ (ContentHeading, ImageSection, ProgressBar, MemberChip)
+│ │ └── ui/ (ContentHeading, ImageSection, ProgressBar, MemberChip, FieldSection)
│ ├── sprint-code/ → 스프린트 코드 도메인
│ │ └── SprintCodeInput
│ ├── peer-comment/ → 피어 코멘트 도메인
@@ -139,7 +139,7 @@ src/
| `Picker` | 목록에서 항목을 선택하는 UI | `PeerMemberPicker` |
| `Chip` | 선택된 항목을 표시하는 태그 UI | `MemberChip` |
| `Repeater` | 동일한 입력 블록을 반복·관리 | `PeerCommentRepeater` |
-| `Block` / `Section` | 여러 요소를 묶은 영역 단위 | `PeerCommentRecipientBlock` |
+| `Block` / `Section` | 여러 요소를 묶은 영역 단위 | `PeerCommentRecipientBlock`, `FieldSection` |
## 🗂️ Import Aliases
@@ -175,6 +175,13 @@ src/
- 인라인 스타일 또는 CSS 모듈(`.module.css`) 혼용 금지
- 새 컴포넌트/페이지 추가 시 같은 경로에 `*.css.ts` 파일 함께 생성
+### 레이아웃 기준선
+
+- `StepLayout` 콘텐츠 영역 좌우 padding: `0 28px` (모든 페이지 공통 기준)
+- `FieldSection`: 논리 블록 하나를 감싸는 padding wrapper (`paddingTop: 24, paddingBottom: 12`)
+ - **하나의 FieldSection = 하나의 논리 블록** (ContentHeading, 개별 폼 필드 등)
+ - `gap` 없음 — 여러 요소의 간격은 각 FieldSection이 담당
+
---
@@ -219,6 +226,10 @@ src/
- 서비스 오류 → `ServiceError` → `/error` 페이지로 이동
- `useErrorHandler` 훅 활용
+### push 전 체크리스트
+
+- CLAUDE.md가 현재 코드 상태를 반영하고 있는지 확인 후 업데이트
+
### 하지 말아야 할 것
- 요청 범위를 벗어난 리팩토링, 기능 추가, 코드 정리
diff --git a/src/components/common/layout/StepLayout.css.ts b/src/components/common/layout/StepLayout.css.ts
index 82cce41..7702b18 100644
--- a/src/components/common/layout/StepLayout.css.ts
+++ b/src/components/common/layout/StepLayout.css.ts
@@ -30,7 +30,8 @@ export const contentSection = style({
overflowY: 'auto',
msOverflowStyle: 'none',
scrollbarWidth: 'none',
- padding: '0 20px',
+ padding: '0 28px',
+ textAlign: 'left',
});
export const contentSectionWithProgress = style({
diff --git a/src/components/common/ui/FieldSection.css.ts b/src/components/common/ui/FieldSection.css.ts
new file mode 100644
index 0000000..68e8c32
--- /dev/null
+++ b/src/components/common/ui/FieldSection.css.ts
@@ -0,0 +1,8 @@
+import { style } from '@vanilla-extract/css';
+
+export const container = style({
+ display: 'flex',
+ flexDirection: 'column',
+ paddingTop: 24,
+ paddingBottom: 12,
+});
diff --git a/src/components/common/ui/FieldSection.tsx b/src/components/common/ui/FieldSection.tsx
new file mode 100644
index 0000000..3ebcca8
--- /dev/null
+++ b/src/components/common/ui/FieldSection.tsx
@@ -0,0 +1,12 @@
+import type { ReactNode } from 'react';
+import * as styles from './FieldSection.css';
+
+interface FieldSectionProps {
+ children: ReactNode;
+}
+
+function FieldSection({ children }: FieldSectionProps) {
+ return
{children}
;
+}
+
+export default FieldSection;
diff --git a/src/components/common/ui/ProgressBar.css.ts b/src/components/common/ui/ProgressBar.css.ts
index e608634..e3b46c3 100644
--- a/src/components/common/ui/ProgressBar.css.ts
+++ b/src/components/common/ui/ProgressBar.css.ts
@@ -3,7 +3,7 @@ import { colors } from '@sopt-makers/colors';
export const container = style({
width: '100%',
- padding: '10px 20px 36px',
+ padding: '16px 20px',
});
export const track = style({
diff --git a/src/components/index.ts b/src/components/index.ts
index 953d153..5bb671c 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -3,6 +3,7 @@ export { default as StepLayout } from './common/layout/StepLayout';
export { default as ProgressBar } from './common/ui/ProgressBar';
export { default as MemberChip } from './common/ui/MemberChip';
export { default as ContentHeading } from './common/ui/ContentHeading';
+export { default as FieldSection } from './common/ui/FieldSection';
export { default as ImageSection } from './common/ui/ImageSection';
export { default as SelectField } from './common/form/SelectField';
export { default as InputField } from './common/form/InputField';
diff --git a/src/components/peer-comment/PeerCommentStepTemplate.css.ts b/src/components/peer-comment/PeerCommentStepTemplate.css.ts
index 57357a4..ac6d676 100644
--- a/src/components/peer-comment/PeerCommentStepTemplate.css.ts
+++ b/src/components/peer-comment/PeerCommentStepTemplate.css.ts
@@ -1,8 +1,7 @@
-import { globalStyle, style } from '@vanilla-extract/css';
+import { style } from '@vanilla-extract/css';
import { colors } from '@sopt-makers/colors';
import { fontsObject } from '@sopt-makers/fonts';
-/** 제목 블록 ↔ 이미지 ↔ 코멘트 반복 영역 사이 16px */
export const stepContent = style({
display: 'flex',
flexDirection: 'column',
@@ -10,21 +9,8 @@ export const stepContent = style({
width: '100%',
});
-/**
- * ContentHeading + 페이지별 안내 문단( children )
- * 제목/설명과 그 아래 문단 사이 간격 없음
- */
-export const headingBlock = style({
- display: 'flex',
- flexDirection: 'column',
- gap: 0,
- width: '100%',
-});
-
-globalStyle(`${headingBlock} p`, {
+export const noticeText = style({
margin: 0,
- marginTop: 0,
- textAlign: 'left',
color: colors.white,
...fontsObject.BODY_3_14_M,
});
diff --git a/src/components/peer-comment/PeerCommentStepTemplate.tsx b/src/components/peer-comment/PeerCommentStepTemplate.tsx
index b36cc70..7d685d2 100644
--- a/src/components/peer-comment/PeerCommentStepTemplate.tsx
+++ b/src/components/peer-comment/PeerCommentStepTemplate.tsx
@@ -1,6 +1,7 @@
import { useCallback, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import ContentHeading from '../common/ui/ContentHeading';
+import FieldSection from '../common/ui/FieldSection';
import ImageSection from '../common/ui/ImageSection';
import StepLayout from '../common/layout/StepLayout';
import PeerCommentRepeater from './PeerCommentRepeater';
@@ -59,10 +60,10 @@ function PeerCommentStepTemplate({
totalSteps={totalSteps}
>
-
+
- 해당 코멘트는 무기명으로 전달되어요.
-
+
해당 코멘트는 무기명으로 전달되어요.
+
{guideImages ? (
diff --git a/src/components/sprint-code/SprintCodeInput.css.ts b/src/components/sprint-code/SprintCodeInput.css.ts
index 3b92fd8..2bf375d 100644
--- a/src/components/sprint-code/SprintCodeInput.css.ts
+++ b/src/components/sprint-code/SprintCodeInput.css.ts
@@ -6,7 +6,6 @@ export const root = style({
display: 'flex',
flexDirection: 'column',
gap: 12,
- padding: '24px 0 12px',
maxWidth: 320,
textAlign: 'left',
});
diff --git a/src/components/sprint-code/SprintCodeInput.tsx b/src/components/sprint-code/SprintCodeInput.tsx
index ad01b4b..ee1245b 100644
--- a/src/components/sprint-code/SprintCodeInput.tsx
+++ b/src/components/sprint-code/SprintCodeInput.tsx
@@ -28,9 +28,7 @@ function CodeInput({
onChange(sanitized);
};
- const activeBoxIndex = isInputFocused
- ? Math.min(value.length, SPRINT_CODE_LENGTH - 1)
- : -1;
+ const activeBoxIndex = isInputFocused ? Math.min(value.length, SPRINT_CODE_LENGTH - 1) : -1;
return (
@@ -75,9 +73,7 @@ function CodeInput({
data-filled={isFilled}
data-error={showError}
>
-
- {digit ?? ''}
-
+ {digit ?? ''}
);
})}
@@ -85,11 +81,7 @@ function CodeInput({
{showError && (
-
+
{errorMessage}
)}
diff --git a/src/pages/MvpPage.css.ts b/src/pages/MvpPage.css.ts
index cb7dcbd..71acaca 100644
--- a/src/pages/MvpPage.css.ts
+++ b/src/pages/MvpPage.css.ts
@@ -2,19 +2,6 @@ import { style } from '@vanilla-extract/css';
import { colors } from '@sopt-makers/colors';
import { fontsObject } from '@sopt-makers/fonts';
-export const body = style({
- display: 'flex',
- flexDirection: 'column',
- gap: 32,
- color: colors.white,
-});
-
-export const fields = style({
- display: 'flex',
- flexDirection: 'column',
- gap: 28,
-});
-
export const fieldGroup = style({
display: 'flex',
flexDirection: 'column',
diff --git a/src/pages/MvpPage.tsx b/src/pages/MvpPage.tsx
index 29fec8b..861a8d1 100644
--- a/src/pages/MvpPage.tsx
+++ b/src/pages/MvpPage.tsx
@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react';
import { useNavigate } from 'react-router-dom';
import { FieldBox } from '@sopt-makers/ui';
import { IconUser, IconXCircle } from '@sopt-makers/icons';
-import { StepLayout, ContentHeading, MemberChip, InputField } from '@components';
+import { StepLayout, ContentHeading, FieldSection, MemberChip, InputField } from '@components';
import { usePeerMembers, useCommentForm } from '@hooks';
import type { PeerMember } from '@types';
import * as styles from './MvpPage.css';
@@ -44,7 +44,7 @@ function MvpPage() {
onNext={handleSubmit}
isNextDisabled={!isAllFilled}
>
-
+
}
/>
+
-
-
-
-
-
-
- ) => setSearchQuery(e.target.value)}
- />
- {searchQuery && (
-
- )}
-
+
+
+
- {searchQuery && filteredMembers.length > 0 && (
-
- {filteredMembers.map((member) => (
- -
-
-
- ))}
-
+
+
+ ) =>
+ setSearchQuery(e.target.value)
+ }
+ />
+ {searchQuery && (
+
)}
- {selectedMember && (
-
-
-
+ {searchQuery && filteredMembers.length > 0 && (
+
+ {filteredMembers.map((member) => (
+ -
+
+
+ ))}
+
)}
-
+ {selectedMember && (
+
+
+
+ )}
-
+
+
+
+
+
);
}
diff --git a/src/pages/SprintCodePage.tsx b/src/pages/SprintCodePage.tsx
index 111b60a..e73ebb9 100644
--- a/src/pages/SprintCodePage.tsx
+++ b/src/pages/SprintCodePage.tsx
@@ -1,6 +1,6 @@
import { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
-import { SprintCodeInput, SPRINT_CODE_LENGTH, StepLayout, ContentHeading } from '@components';
+import { SprintCodeInput, SPRINT_CODE_LENGTH, StepLayout, ContentHeading, FieldSection } from '@components';
import { getSprintInfoByCode } from '@lib/api/sprint';
import { useCommentForm, useErrorHandler } from '@hooks';
import { callApi } from '@lib/apiClient';
@@ -40,16 +40,20 @@ function SprintCodePage() {
currentStep={0}
totalSteps={6}
>
-
-
-
{
- setCode(next);
- setShowError(false);
- }}
- showError={showError}
- />
+
+
+
+
+
+ {
+ setCode(next);
+ setShowError(false);
+ }}
+ showError={showError}
+ />
+
);
}
diff --git a/src/pages/UserInfoPage.css.ts b/src/pages/UserInfoPage.css.ts
index 16152a4..410ba46 100644
--- a/src/pages/UserInfoPage.css.ts
+++ b/src/pages/UserInfoPage.css.ts
@@ -2,9 +2,8 @@ import { style } from '@vanilla-extract/css';
import { colors } from '@sopt-makers/colors';
import { fontsObject } from '@sopt-makers/fonts';
-export const body = style({
- display: 'flex',
- flexDirection: 'column',
+export const noticeText = style({
+ margin: 0,
textAlign: 'left',
color: colors.white,
...fontsObject.BODY_3_14_M,
@@ -13,10 +12,3 @@ export const body = style({
export const nameWidth = style({
width: '50%',
});
-
-export const inputWrapper = style({
- display: 'flex',
- flexDirection: 'column',
- gap: 36,
- marginTop: 24,
-});
diff --git a/src/pages/UserInfoPage.tsx b/src/pages/UserInfoPage.tsx
index 86a4606..2499f90 100644
--- a/src/pages/UserInfoPage.tsx
+++ b/src/pages/UserInfoPage.tsx
@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
-import { StepLayout, ContentHeading, SelectField } from '@components';
+import { StepLayout, ContentHeading, FieldSection, SelectField } from '@components';
import * as styles from './UserInfoPage.css';
import { TextField, useToast } from '@sopt-makers/ui';
import { getChapterCodes, getTeamCodes } from '@lib/api/chapter';
@@ -56,54 +56,60 @@ function UserInfoPage() {
currentStep={1}
totalSteps={6}
>
-
-
-
+
+
+
본인의 이름은 너목들의 관리자만 확인할 수 있으며,
모든 코멘트는 무기명으로 전달되어요.
+
-
- {
- setName(e.target.value);
- setIsError(false);
- }}
- />
- {
- setChapterCode(value);
- setIsError(false);
- }}
- />
- {
- setTeamCode(value);
- setIsError(false);
- }}
- />
-
-
+
+ {
+ setName(e.target.value);
+ setIsError(false);
+ }}
+ />
+
+
+
+ {
+ setChapterCode(value);
+ setIsError(false);
+ }}
+ />
+
+
+
+ {
+ setTeamCode(value);
+ setIsError(false);
+ }}
+ />
+
);
}