-
Notifications
You must be signed in to change notification settings - Fork 4
refactor: 밸런스 게임 서브 태그 모달 ui 및 로직 수정 #310
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
Conversation
Walkthrough이번 PR은 GameTagModal 컴포넌트의 스타일과 기능을 개선하는 변경사항을 포함합니다. GameTagModal의 스타일 파일에서 간격 조정 및 새로운 스타일, 함수 추가가 이루어졌으며, 컴포넌트 파일에서는 서브태그 입력 관리, 최대 길이 제한, 스페이스바로 서브태그 추가 및 삭제 기능이 도입되었습니다. 그 외에 BalanceGame 관련 컴포넌트 및 스타일, 스토리 파일, 색상 상수와 유틸 함수가 추가 또는 업데이트되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as 사용자
participant M as GameTagModal
U->>M: 서브태그 입력 (handleInputChange 호출)
M->>M: 입력 값 업데이트 및 최대 10자 제한 적용
M->>M: useEffect로 서브태그 값 재구성
U->>M: 스페이스바 입력 (handleKeyUp 호출)
M->>M: handleSpaceAction으로 서브태그 추가
U->>M: 삭제 요청 (handleDeleteSubTag 호출)
M->>M: 서브태그 제거 진행
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/utils/array.ts (1)
13-15: 새로운 유틸리티 함수가 적절히 구현되었습니다
createArrayFromCommaString함수는 서버에서 받은 쉼표로 구분된 서브 태그 문자열을 배열로 변환하는 용도로 적절히 구현되었습니다. 입력값이 없는 경우(falsy)를 빈 배열로 처리하는 방식도 올바릅니다.다만, 문자열 앞뒤의 공백을 처리하는 기능이 추가되면 더 견고해질 수 있습니다:
-export const createArrayFromCommaString = (str: string): string[] => { - return str ? str.split(',') : []; +export const createArrayFromCommaString = (str: string): string[] => { + return str ? str.split(',').map(item => item.trim()) : []; };src/stories/mobile/molecules/GameTagModal.stories.tsx (1)
10-11: 예제 데이터가 실제 사용 사례를 반영하도록 개선되었습니다스토리북의 예제 데이터를 실제 사용 사례(한국어 '커플' 태그)로 변경한 것은 적절합니다. 긴 서브 태그 문자열은 PR 목표에 언급된 "텍스트가 10자를 초과할 경우 도움말 메시지 표시" 기능을 테스트하는 데 유용합니다.
테스트를 더 완성도 있게 하려면 여러 서브 태그를 포함하는 쉼표로 구분된 문자열로 구성하는 것도 고려해보세요:
- subTag: '커플커플커플커플커플', + subTag: '커플,데이트,연애',src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (2)
63-68: 입력 래퍼의 높이 값이 하드코딩되어 있습니다.inputWrapper의 고정 높이(67px)가 유연성을 제한할 수 있습니다. 내용의 크기에 따라 자동으로 조정되는 방식을 고려해 보세요.
export const inputWrapper = css({ display: 'flex', flexDirection: 'column', - height: '67px', + minHeight: '67px', gap: '4px', });
81-87: 서브태그 버튼 스타일링의 접근성을 고려해 보세요.버튼에
all: 'unset'을 사용하면 기본 접근성 기능(포커스 표시기 등)이 제거될 수 있습니다. 접근성을 위해:focus상태를 명시적으로 정의하는 것이 좋습니다.export const subTagButtonStyling = css({ all: 'unset', display: 'flex', color: color.MAIN, fontSize: '12px', cursor: 'pointer', + ':focus': { + outline: `2px solid ${color.MAIN_2}`, + borderRadius: '2px', + }, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts(3 hunks)src/components/mobile/molecules/GameTagModal/GameTagModal.tsx(3 hunks)src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx(2 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts(1 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx(3 hunks)src/hooks/api/game/useCreateGameMutation.ts(1 hunks)src/stories/mobile/molecules/GameTagModal.stories.tsx(1 hunks)src/styles/color.ts(1 hunks)src/utils/array.ts(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🔇 Additional comments (26)
src/styles/color.ts (1)
3-3:Details
❓ Verification inconclusive
중복 색상 값이 있습니다:
MAIN_2와SKYBLUE
MAIN_2의 색상 값('#9DB7FF')은 이미 19번째 줄에 있는SKYBLUE색상과 동일합니다. 색상 값의 일관성과 유지보수를 위해 기존 색상 상수를 재사용하는 것이 좋습니다.다음과 같이 확인해주세요:
🏁 Script executed:
#!/bin/bash # SKYBLUE 색상 상수 사용처 확인 rg "color\.SKYBLUE" --type ts --type tsx # 새로 추가된 MAIN_2 색상 상수 사용처 확인 rg "color\.MAIN_2" --type ts --type tsxLength of output: 140
중복 색상 상수 확인 및 추가 검증 요청
- 현재
src/styles/color.ts파일에서MAIN_2: '#9DB7FF'으로 정의된 색상 값이 19번째 줄의SKYBLUE와 동일합니다.- 색상 일관성과 유지보수를 위해 중복된 상수 대신 기존의
SKYBLUE상수를 재사용하는 것이 좋겠습니다.- 단, 기존에 제공된 ripgrep 명령어 실행 시 tsx 파일에 대해 “unrecognized file type: tsx” 오류가 발생하여 전체 사용처에 대한 자동 검증이 제대로 이루어지지 않았습니다.
- 아래 스크립트를 실행하여 .ts 및 .tsx 파일 모두에서
color.SKYBLUE와color.MAIN_2의 사용처를 수동으로 재검증해 주시기 바랍니다:#!/bin/bash echo "TS 파일 검색:" rg "color\.SKYBLUE" -g "*.ts" rg "color\.MAIN_2" -g "*.ts" echo "TSX 파일 검색:" rg "color\.SKYBLUE" -g "*.tsx" rg "color\.MAIN_2" -g "*.tsx"검증 후, 불필요한 상수 중복을 해결하기 위한 리팩토링(예:
MAIN_2제거 또는SKYBLUE재사용 적용)을 진행해 주시기 바랍니다.src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts (1)
81-81: 서브 태그 래퍼에 flex-wrap 속성 추가는 적절합니다여러 서브 태그가 있을 때 자연스럽게 다음 줄로 넘어갈 수 있도록
flexWrap: 'wrap'속성을 추가한 것은 모바일 화면에서 태그 표시를 개선하는 좋은 변경입니다. 화면 너비가 제한된 모바일 환경에서 특히 유용할 것입니다.src/hooks/api/game/useCreateGameMutation.ts (1)
24-24: 성공 메시지 상수 업데이트가 적절합니다.
SUCCESS.CREATEGAME.CREATE에서SUCCESS.GAME.CREATE로의 변경은 메시지 상수 구조를 더 간결하게 만들어 일관성을 향상시킵니다.src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (3)
5-5: 유틸리티 함수 가져오기가 적절합니다.
createArrayFromCommaString유틸리티 함수를 가져와서 서브태그 배열로 변환하는 로직이 잘 구현되었습니다.
60-60: 서브태그 배열 변환 구현이 적절합니다.쉼표로 구분된 문자열을 배열로 변환하여 서브태그를 처리하는 방식이 잘 구현되었습니다.
170-171: 서브태그 렌더링 로직이 잘 개선되었습니다.이전에는 단일 태그만 표시했지만, 이제 배열을 사용하여 여러 서브태그를 렌더링하도록 개선되었습니다.
src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx (2)
53-53: setSubTagValue prop 업데이트가 적절합니다.
onChange대신setEach함수를 사용하도록 변경하여 GameTagModal 컴포넌트의 변경된 인터페이스와 일관성을 유지합니다.
82-83: null 체크 추가로 안전한 코드 처리가 개선되었습니다.
fileId가 undefined일 경우 null을 사용하도록 하는 nullish 병합 연산자(??)를 추가하여 오류 발생 가능성을 줄였습니다.src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (11)
10-11: 필요한 유틸리티와 훅 가져오기가 적절합니다.
createArrayFromCommaString과useOutsideClick훅을 가져와서 서브태그 기능을 구현하는 데 필요한 기능을 추가했습니다.
19-19: setSubTagValue 함수 서명 변경이 적절합니다.이벤트 객체 대신 직접 name과 tag 매개변수를 받도록 변경하여 더 명확하고 직관적인 API를 제공합니다.
31-36: 상태 관리 로직이 잘 설계되었습니다.입력 참조와 서브태그 배열 상태를 관리하기 위한 로직이 잘 구현되었습니다.
37-38: 입력값과 오류 상태 관리가 적절합니다.입력값을 추적하고 오류 상태를 관리하기 위한 상태 변수를 추가하여 사용자 경험을 개선했습니다.
40-45: useEffect를 통한 서브태그 값 동기화 로직이 잘 구현되었습니다.입력값과 현재 서브태그 배열을 기반으로 최종 서브태그 값을 계산하고 업데이트하는 로직이 효과적으로 구현되었습니다.
47-54: 입력 제한 및 오류 처리 로직이 잘 구현되었습니다.10자 제한을 적용하고 오류 상태를 관리하는 로직이 사용자 경험을 향상시킵니다.
56-64: 스페이스바 액션 및 외부 클릭 처리가 잘 구현되었습니다.스페이스바를 누르거나 입력 영역 외부를 클릭할 때 태그를 추가하는 기능이 요구사항에 맞게 잘 구현되었습니다.
65-77: 키보드 이벤트 처리 로직이 적절합니다.스페이스바 키 입력을 감지하고 처리하는 로직과 입력 길이 검증을 통한 오류 상태 설정이 잘 구현되었습니다.
83-85: 서브태그 삭제 기능이 적절하게 구현되었습니다.특정 인덱스의 서브태그를 필터링하여 제거하는 함수가 깔끔하게 구현되었습니다.
87-95: 태그 제출 로직이 개선되었습니다.메인 태그 존재 여부와 유효성 검사를 통해 제출 전 데이터 검증을 수행하는 로직이 잘 구현되었습니다.
122-169: 서브태그 UI 구현이 요구사항에 맞게 잘 개선되었습니다.서브태그 입력과 관련된 UI가 다음 요구사항을 잘 충족합니다:
- 최대 3개까지 서브태그 추가 가능
- 각 태그에 삭제 버튼 제공
- 3개가 되면 입력 필드 숨김
- 오류 메시지 표시
- 등록 버튼 상태 관리
🧰 Tools
🪛 Biome (1.9.4)
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.(lint/correctness/useJsxKeyInIterable)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (7)
9-9: 간격 조정이 디자인 일관성을 향상시킵니다.컨텐츠 래퍼의 간격을 20px에서 16px로 줄임으로써 모바일 화면에서 더 적합한 컴팩트한 레이아웃을 제공합니다.
27-29: 서브태그를 위한 텍스트 스타일 추가가 적절합니다.새로운 서브태그 텍스트 스타일이 기존 타이포그래피 체계와 잘 통합되어 있으며, GY[2] 색상을 사용하여 시각적 계층구조를 명확히 합니다.
35-37: 오류 메시지 스타일링이 명확합니다.빨간색을 사용한 오류 메시지 스타일링은 사용자에게 문제 상황을 직관적으로 전달하는 데 효과적입니다.
43-44: 태그 래퍼 간격 증가로 가독성이 향상되었습니다.태그 요소들 사이의 간격을 6px에서 8px로 늘림으로써 시각적 구분이 더 명확해져 사용자 경험이 개선되었습니다.
46-50: 태그 바텀 래퍼 추가가 적절합니다.새로 추가된 tagBottomWrapper는 모달 하단 요소들을 구조화하는 데 도움이 되며, 전체 너비를 사용하여 일관된 레이아웃을 제공합니다.
70-79: 서브태그 칩 스타일링이 잘 정의되어 있습니다.서브태그 칩 스타일링은 시각적으로 잘 구분되며, 메인 컬러와 조화로운 배경색을 사용하여 일관된 디자인 시스템을 유지합니다.
89-97: 서브태그 래퍼 스타일 함수가 동적 패딩을 적절히 처리합니다.isTagMax 상태에 따라 패딩을 동적으로 조정하는 방식은 태그 개수에 따른 UI 일관성을 유지하는 데 효과적입니다. flexWrap: 'wrap' 속성을 사용하여 여러 태그를 자연스럽게 배치할 수 있게 한 점도 좋습니다.
| <div css={S.subTagChipStyling}> | ||
| <span>#{tag}</span> | ||
| <button | ||
| type="button" | ||
| css={S.subTagButtonStyling} | ||
| onClick={() => handleDeleteSubTag(idx)} | ||
| > | ||
| ⨉ | ||
| </button> | ||
| </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.
key prop 누락 수정 필요
반복 렌더링되는 요소에 React key 속성이 누락되었습니다. 각 태그 항목을 고유하게 식별할 수 있도록 key 속성을 추가해주세요.
- <div css={S.subTagChipStyling}>
+ <div key={idx} css={S.subTagChipStyling}>
<span>#{tag}</span>
<button
type="button"
css={S.subTagButtonStyling}
onClick={() => handleDeleteSubTag(idx)}
>
⨉
</button>
</div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} | |
| <div key={idx} css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} |
🧰 Tools
🪛 Biome (1.9.4)
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
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.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (1)
132-132:⚠️ Potential issuegetButtonStyling 함수의 border 속성에 오류가 있습니다.
isSelected가 true일 때 border 값이 "null"이라는 문자열로 설정되어 있습니다. 이는 CSS에서 유효하지 않은 값입니다. null 값을 사용하려면 다음과 같이 수정해야 합니다.
- border: isSelected ? 'null' : `1px solid ${color.GY[4]}`, + border: isSelected ? 'none' : `1px solid ${color.GY[4]}`,
🧹 Nitpick comments (1)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (1)
40-45: useEffect 의존성 배열 관리 필요useEffect의 의존성 배열에 포함된 값들을 검토해주세요. 특히
setCurrentSubTag는 useState에서 반환된 setter 함수이므로 의존성 배열에 포함할 필요가 없습니다. 또한 ESLint 규칙을 비활성화했는데, 대신 의존성 문제를 제대로 해결하는 것이 좋습니다.- useEffect(() => { - const subTagList = inputValue - ? [...currentSubTag, inputValue] - : currentSubTag; - setSubTagValue('subTag', subTagList.join(',')); - }, [currentSubTag, setCurrentSubTag, inputValue, setSubTagValue]); + useEffect(() => { + const subTagList = inputValue + ? [...currentSubTag, inputValue] + : currentSubTag; + setSubTagValue('subTag', subTagList.join(',')); + }, [currentSubTag, inputValue, setSubTagValue]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts(3 hunks)src/components/mobile/molecules/GameTagModal/GameTagModal.tsx(3 hunks)src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx(2 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts(1 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx(3 hunks)src/hooks/api/game/useCreateGameMutation.ts(1 hunks)src/stories/mobile/molecules/GameTagModal.stories.tsx(1 hunks)src/styles/color.ts(1 hunks)src/utils/array.ts(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🔇 Additional comments (30)
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts (1)
79-84: flexWrap 속성 추가로 UI 레이아웃 개선이 이루어졌습니다.하위 태그들이 여러 줄로 자연스럽게 배치될 수 있도록
flexWrap: 'wrap'속성을 추가하셨네요. 이 변경으로 태그가 많아져도 UI가 깨지지 않고 유연하게 대응할 수 있게 되었습니다.src/styles/color.ts (1)
2-3: 새로운 색상 MAIN_2 추가가 잘 되었습니다.색상 팔레트에 새로운 색상
MAIN_2를 추가하셨네요. 이 색상은 기존 메인 색상과 조화를 이루면서 서브 태그 모달 UI에 활용될 수 있을 것 같습니다.src/hooks/api/game/useCreateGameMutation.ts (1)
24-24: 성공 메시지 상수 변경이 잘 이루어졌습니다.성공 메시지를
SUCCESS.CREATEGAME.CREATE에서SUCCESS.GAME.CREATE로 변경하셨네요. 네이밍 컨벤션이 더 일관성 있게 개선되었습니다.src/stories/mobile/molecules/GameTagModal.stories.tsx (1)
9-11: Storybook 예제 데이터의 한글화가 잘 이루어졌습니다.예시 게임 데이터의 태그 값을 한글로 변경하셨네요. '커플'이라는 태그와 긴 서브태그 텍스트는 PR의 핵심 기능인 태그 블록 생성 및 길이 제한 로직을 테스트하기에 적합해 보입니다.
다만, 서브태그 문자열이 10글자를 초과하는 것으로 보이는데, PR 설명에 따르면 10글자 초과 시 도움말 메시지가 표시된다고 했습니다. 이것이 의도적인 테스트 케이스인지 확인해 보시면 좋겠습니다.
src/utils/array.ts (1)
13-15: 코드가 간결하고 목적에 잘 부합합니다.함수의 코드가 간결하면서도 목적에 맞게 잘 구현되었습니다. falsy 값 처리를 통한 방어적 프로그래밍이 좋은 접근 방식입니다.
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (3)
5-5: import 구문이 적절히 추가되었습니다.새로운 유틸리티 함수의 import가 적절하게 추가되었습니다.
60-60: 서브 태그 처리 로직이 개선되었습니다.문자열을 배열로 변환하는 유틸리티 함수를 활용하여 서브 태그를 효율적으로 처리하고 있습니다. 기본값으로 빈 문자열을 설정한 것도 잘했습니다.
170-171: 서브 태그 렌더링 로직이 개선되었습니다.이전에는 단일 태그로 처리되던 것을 배열로 변환하여 각각의 서브 태그를 개별적으로 렌더링하도록 개선되었습니다. 조건부 처리와 map 함수 활용이 적절합니다.
src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx (2)
53-53: setSubTagValue prop 전달 방식이 개선되었습니다.이전의 onChange 함수 대신 setEach 함수를 사용하여 서브 태그 값을 설정하는 방식으로 변경되었습니다. 이는 GameTagModal 컴포넌트의 변경된 인터페이스와 일치합니다.
82-83: 방어적 프로그래밍이 적용되었습니다.fileId가 undefined일 경우를 대비하여 null 값을 제공하는 코드가 추가되었습니다. 이러한 방어적 프로그래밍은 런타임 오류를 방지하는 데 도움이 됩니다.
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (11)
2-11: 필요한 임포트가 적절히 추가되었습니다.컴포넌트의 새로운 기능을 위한 임포트가 잘 추가되었습니다. 특히 유틸리티 함수와 커스텀 훅을 활용한 점이 좋습니다.
19-19: 함수 시그니처가 명확하게 개선되었습니다.setSubTagValue의 함수 시그니처가 변경되어 이벤트 객체 대신 직접 name과 tag 값을 받도록 개선되었습니다. 이는 더 명확하고 사용하기 쉬운 인터페이스를 제공합니다.
31-39: 상태 관리가 체계적으로 구성되었습니다.입력 필드 참조와 다양한 상태 변수를 통해 서브 태그 관리를 체계적으로 구현했습니다. 초기 상태 설정 로직이 깔끔합니다.
47-54: 입력 검증 로직이 적절하게 구현되었습니다.10자 제한을 확인하고 오류 상태를 관리하는 로직이 잘 구현되었습니다. 입력값 길이 초과 시 즉시 반환하여 추가 처리를 방지하는 방어적 접근법이 좋습니다.
56-63: 스페이스 키 처리 로직이 명확합니다.스페이스 키 입력 시 태그 블록 생성 로직이 PR 목표에 맞게 잘 구현되었습니다. 빈 문자열 확인과 trim 처리를 통해 예외 상황도 잘 관리하고 있습니다.
65-77: 키 이벤트 처리 로직이 명확합니다.키 입력 이벤트 처리 로직이 잘 구현되었습니다. 스페이스 키 감지와 길이 제한 확인이 적절하게 구현되었습니다.
83-85: 태그 삭제 기능이 간결하게 구현되었습니다.서브 태그 삭제 기능이 명확하고 간결하게 구현되었습니다. 불변성을 유지하면서 기존 배열에서 항목을 제거하는 패턴을 잘 활용했습니다.
87-95: 폼 제출 로직이 검증 과정을 포함하여 개선되었습니다.태그 제출 시 유효성 검사를 수행하는 로직이 추가되었습니다. 메인 태그 존재 여부 확인과 전체 태그 유효성 검사를 통해 안전한 제출 처리를 구현했습니다.
141-158: 조건부 입력 필드 렌더링이 요구사항에 맞게 구현되었습니다.서브 태그가 3개인 경우 입력 필드를 숨기고, 그렇지 않은 경우에만 표시하는 조건부 렌더링이 PR 목표에 맞게 구현되었습니다. 오류 메시지도 적절히 표시되고 있습니다.
159-168: 등록 버튼이 적절한 비활성화 조건과 함께 구현되었습니다.태그 등록 버튼이 메인 태그 존재 여부에 따라 활성화/비활성화되도록 구현되었습니다. 시각적 상태 표시와 기능적 제한이 모두 적용되어 있습니다.
1-1:Details
❓ Verification inconclusive
ESLint 규칙 비활성화 사용 이유 확인 필요
ESLint 규칙 비활성화 주석이 추가되었습니다. 특별한 이유 없이 규칙을 비활성화하는 것은 코드 품질 유지에 좋지 않으니, 정말 필요한지 확인해주세요.
🌐 Web query:
React useEffect 의존성 배열에 함수를 넣는 것의 권장 방식은 무엇인가요?💡 Result:
React의
useEffect훅에서 의존성 배열에 함수를 포함할 때는 함수의 메모이제이션 여부와 참조 동일성 보장이 핵심입니다. 다음은 권장되는 방식입니다:1. useCallback으로 함수 메모이제이션
의존성 배열에 추가할 함수를
useCallback으로 래핑해 참조 동일성을 보장해야 합니다[3][7][9].const memoizedFn = useCallback(() => { // 함수 로직 }, [dependencies]);이렇게 하면:
dependencies가 변경될 때만 새 함수 생성- 컴포넌트 리렌더링 시 불필요한 함수 재생성 방지
- 의존성 배열에 안전하게 추가 가능[3][9]
2. 의존성 배열에 추가하는 방식
메모이제이션된 함수를 의존성 배열에 포함:
useEffect(() => { memoizedFn(); }, [memoizedFn]);3. 함수 위치에 따른 처리
- 컴포넌트 외부 함수: 의존성 배열에서 제외 가능 (참조가 고정)[3]
- 이펙트 내부 함수: 의존성 배열에 추가할 필요 없음[4]
- props로 전달된 함수: 반드시 의존성 배열에 포함[7][9]
4. 주의사항
// ❌ 위험한 패턴 (함수 재생성시 무한 루프) useEffect(() => { nonMemoizedFn(); }, [nonMemoizedFn]); // ✅ 안전한 패턴 useEffect(() => { memoizedFn(); }, [memoizedFn]);5. 최적화 전략
상황 처리 방식 API 호출 함수 useCallback으로 래핑 후 의존성 추가[9]이벤트 핸들러 이펙트 내부에서 핸들러 정의[4] props 함수 상위 컴포넌트에서 메모이제이션 필수[3] 함수를 의존성 배열에 추가할 때는 반드시 참조 안정성을 고려해야 하며, ESLint의
exhaustive-deps규칙을 활성화해 누락된 의존성을 자동으로 검출하는 것이 좋습니다[9][10].Citations:
- 1: https://taak-e.tistory.com/entry/useEffect-%EC%9D%98%EC%A1%B4%EC%84%B1%EB%B0%B0%EC%97%B4-%ED%99%9C%EC%9A%A9
- 2: https://jhbljs92.tistory.com/entry/7-useEffect
- 3: https://chamdom.blog/what-is-dependency-array/
- 4: https://simsimjae.tistory.com/401
- 5: https://tocomo.tistory.com/32
- 6: https://velog.io/@ehhdrud/useEffect%EC%9D%98-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%B0%B0%EC%97%B4-%EA%B4%80%EB%A0%A8-%EA%B2%BD%EA%B3%A0
- 7: https://www.jinlog.org/8375f26e-57eb-4c8b-899a-f9fee9982033
- 8: https://cocoon1787.tistory.com/796
- 9: https://velog.io/@narcoker/React-useEffect-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0
- 10: https://velog.io/@badassong/%EB%93%9C%EC%BD%9406-useEffect-%EC%9D%98%EC%A1%B4%EC%84%B1-%EB%B0%B0%EC%97%B4%EC%97%90-%EC%B6%94%EA%B0%80%ED%95%B4%EB%B3%B4%EC%9E%90
ESLint 규칙 비활성화 이유 확인 요청
현재 파일(
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx, 1번 라인)에서/* eslint-disable react-hooks/exhaustive-deps */주석이 사용되었습니다. React의 useEffect 의존성 배열에서 함수를 사용할 경우, 함수의 메모이제이션(useCallback)과 참조 안정성 보장이 매우 중요한데, 이 점을 감안하면 해당 규칙을 비활성화하기보다는 아래와 같은 방식을 고려해보시기 바랍니다:
- 메모이제이션 사용: 의존성 배열에 포함되는 함수는
useCallback으로 감싸서 참조 동일성을 유지하고 불필요한 재생성을 방지합니다.const memoizedFn = useCallback(() => { // 함수 로직 }, [dependencies]); useEffect(() => { memoizedFn(); }, [memoizedFn]);- 비활성화 이유 명시: 만약 정말로 ESLint 규칙을 비활성화해야 하는 특별한 케이스라면, 주석에 그 이유를 구체적으로 명시해 주세요.
위 사항을 검토하시어, 불필요한 규칙 비활성화가 없는지 다시 한번 확인해주시기 바랍니다.
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (9)
9-9: 간격 조정이 UI 일관성을 개선했습니다.contentWrapper의 gap을 20px에서 16px로 줄여 UI 요소 간의 간격을 더 조밀하게 만들었습니다. 이는 모바일 환경에서 더 나은 사용자 경험을 제공할 것입니다.
27-29: 서브 태그 텍스트 스타일링 추가 - 좋은 접근입니다.서브 태그에 대한 일관된 텍스트 스타일을 정의했습니다. Mobile.Main.Regular_12 타이포그래피를 적용하고 GY[2] 색상을 사용하여 시각적으로 구분되는 서브 태그 텍스트를 만들었습니다.
35-37: 오류 메시지 스타일링 추가 - 사용자 피드백에 적합합니다.오류 메시지를 위한 일관된 스타일을 정의했습니다. PR 목표에 명시된 "텍스트가 10자를 초과할 때 나타나는 도움말 메시지"를 위한 스타일링으로 적절합니다.
43-44: 태그 간격 개선으로 가독성 향상.tagWrapper의 gap을 6px에서 8px로 늘려 태그 항목 간의 가독성과 사용성이 향상되었습니다.
46-50: 태그 하단 래퍼 스타일 추가 - 레이아웃 구조화에 도움.새로운 tagBottomWrapper 스타일은 컴포넌트의 하단 부분에 대한 레이아웃을 명확하게 정의합니다. 이는 서브 태그 입력 영역의 구조화에 도움이 될 것입니다.
63-68: 입력 래퍼 스타일 추가 - 구조적 개선.입력 필드와 관련 요소를 감싸는 컨테이너 스타일을 정의했습니다. 고정된 height는 컨텐츠가 추가되거나 제거될 때 레이아웃 이동을 방지하는 데 도움이 됩니다.
70-79: 서브 태그 칩 스타일링 - 시각적 일관성 확보.서브 태그 칩에 대한 스타일링이 잘 정의되었습니다. MAIN_2 색상의 테두리와 WT_VIOLET 배경색을 사용하여 메인 테마와 일관된 디자인을 유지하면서도 시각적으로 구분됩니다.
81-87: 서브 태그 버튼 스타일링 - 사용성 개선.버튼 스타일을 'unset'하고 커스텀 스타일을 적용한 접근은 적절합니다. 커서를 'pointer'로 설정하여 클릭 가능한 요소임을 사용자에게 명확히 전달합니다.
89-97: 동적 패딩을 활용한 서브 태그 래퍼 - 탁월한 구현.isTagMax 매개변수를 기반으로 paddingBottom을 동적으로 조정하는 접근은 PR 목표에 명시된 "3개의 태그 블록이 있을 때 입력 필드가 사라지고, 블록 수가 2개 이하로 줄어들면 다시 나타나는" 기능을 효과적으로 지원합니다.
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | ||
| {currentSubTag.map((tag, idx) => ( | ||
| <div css={S.subTagChipStyling}> | ||
| <span>#{tag}</span> | ||
| <button | ||
| type="button" | ||
| css={S.subTagButtonStyling} | ||
| onClick={() => handleDeleteSubTag(idx)} | ||
| > | ||
| ⨉ | ||
| </button> | ||
| </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.
서브 태그 렌더링에 key 속성이 누락되었습니다.
map 함수로 서브 태그를 렌더링할 때 각 요소에 고유한 key 속성이 필요합니다. React의 렌더링 최적화와 DOM 업데이트를 위해 key를 추가해주세요.
- {currentSubTag.map((tag, idx) => (
- <div css={S.subTagChipStyling}>
+ {currentSubTag.map((tag, idx) => (
+ <div key={`${tag}-${idx}`} css={S.subTagChipStyling}>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | |
| {currentSubTag.map((tag, idx) => ( | |
| <div css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} | |
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | |
| {currentSubTag.map((tag, idx) => ( | |
| <div key={`${tag}-${idx}`} css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} |
🧰 Tools
🪛 Biome (1.9.4)
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (1)
1-2: React Hook 경고 비활성화 및 필요한 훅 임포트 추가React Hook 의존성 경고를 비활성화하고 필요한 훅(
useRef,useState)을 추가했습니다. 가능하다면 경고를 비활성화하는 대신 의존성 배열을 적절히 수정하는 것이 좋습니다.-/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect, useRef, useState } from 'react';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts(3 hunks)src/components/mobile/molecules/GameTagModal/GameTagModal.tsx(3 hunks)src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx(2 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts(1 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx(3 hunks)src/hooks/api/game/useCreateGameMutation.ts(1 hunks)src/stories/mobile/molecules/GameTagModal.stories.tsx(1 hunks)src/styles/color.ts(1 hunks)src/utils/array.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/stories/mobile/molecules/GameTagModal.stories.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
🔇 Additional comments (27)
src/styles/color.ts (1)
3-3: 새로운 색상 정의가 추가되었네요!새로운 색상인
MAIN_2가 색상 팔레트에 추가되었습니다. 서브 태그 모달 UI에 사용될 것으로 보입니다.src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts (1)
81-81: 서브 태그 래퍼에 flexWrap 속성 추가는 좋은 개선입니다.
flexWrap: 'wrap'속성 추가를 통해 서브 태그가 컨테이너 너비를 초과할 경우 자연스럽게 다음 줄로 넘어갈 수 있게 되었습니다. 이는 여러 개의 서브 태그를 표시할 때 레이아웃을 개선하는 데 도움이 됩니다.src/utils/array.ts (1)
13-15: 쉼표로 구분된 문자열을 배열로 변환하는 유틸리티 함수 추가이 새로운 유틸리티 함수는 PR 목표에서 언급된 대로 서버가 쉼표로 구분된 단일 문자열로 처리하는 서브 태그 값을 프론트엔드에서 배열로 변환하는 데 사용됩니다. 빈 문자열 또는 null 처리도 잘 구현되어 있습니다.
src/hooks/api/game/useCreateGameMutation.ts (1)
24-24:Details
✅ Verification successful
성공 메시지 상수 업데이트
성공 메시지 상수가
SUCCESS.CREATEGAME.CREATE에서SUCCESS.GAME.CREATE로 변경되었습니다. 이는 메시지 상수의 표준화를 위한 변경으로 보입니다. 메시지 상수 구조가 변경되었을 수 있으니 다른 부분에서도 일관되게 적용되었는지 확인해주세요.
🏁 Script executed:
#!/bin/bash # 메시지 상수의 사용 패턴 확인 rg -A 2 "SUCCESS\.(CREATEGAME|GAME)\.CREATE" --glob "*.{ts,tsx}"Length of output: 622
성공 메시지 상수 업데이트 확인
SUCCESS.GAME.CREATE상수가src/hooks/api/game/useCreateGameMutation.ts와src/pages/BalanceGameCreationPage/BalanceGameCreationPage.tsx에서 일관성 있게 사용되고 있음을 확인했습니다.- 상수의 구조가 표준화되어 적용되었으므로 다른 부분에서도 문제가 없어 보입니다.
src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.tsx (3)
5-5: 새 유틸리티 함수 추가 완료콤마로 구분된 문자열을 배열로 변환하는 유틸리티 함수를 임포트하여 서브 태그 처리를 개선했습니다.
60-60: 서브 태그 변환 로직 개선서브 태그 문자열을 배열로 변환하는 로직이 적절히 구현되었습니다. 기본값으로 빈 문자열을 제공하여
undefined처리도 잘 되어 있습니다.
170-171: 서브 태그 렌더링 개선단일 태그 대신 서브 태그 배열을 매핑하여 개별
GameTagChip컴포넌트로 렌더링하는 방식으로 변경되었습니다. 이제 여러 서브 태그를 올바르게 표시할 수 있습니다.src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx (2)
53-53: 서브 태그 값 설정 방식 변경
GameTagModal컴포넌트에 전달하는setSubTagValueprop을onChange에서setEach로 변경하여 수정된 서브 태그 처리 방식과 일관성을 유지했습니다.
82-83: 이미지 삭제 로직의 안정성 개선Nullish 병합 연산자(
??)를 추가하여fileId가undefined일 경우null을 전달하도록 개선했습니다. 이 변경으로handleDeleteImg함수가 항상 유효한 값을 받을 수 있게 되었습니다.src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (9)
9-11: 필요한 유틸리티 및 훅 임포트 추가밸런스 게임 폼 검증, 배열 유틸리티, 외부 클릭 감지 훅 등 필요한 기능들을 적절히 임포트했습니다.
19-19: 서브 태그 설정 함수 시그니처 개선
setSubTagValue함수의 시그니처를 이벤트 객체 대신 이름과 태그 문자열을 직접 받도록 변경하여 더 명확하고 일관된 방식으로 값을 설정할 수 있게 되었습니다.
31-38: 입력 참조 및 상태 관리 구현입력 요소에 대한 참조와 서브 태그 배열, 입력 값, 오류 상태 등을 관리하기 위한 상태들이 잘 구현되었습니다.
createArrayFromCommaString유틸리티 함수를 사용하여 초기 서브 태그 배열을 생성하는 방식이 효율적입니다.
40-45: 서브 태그 값 업데이트 로직 구현입력 값이 변경될 때마다 서브 태그 값을 업데이트하는 로직이 잘 구현되었습니다. 입력 필드의 값과 현재 서브 태그 배열을 결합하여 최종 문자열로 변환하는 방식이 적절합니다.
47-54: 입력 변경 처리 함수 구현입력 값의 길이를 검증하고 오류 상태를 관리하는 함수가 잘 구현되었습니다. 10자 제한을 초과하는 입력을 방지하는 로직이 적절합니다.
56-63: 스페이스 액션 처리 및 외부 클릭 감지 구현스페이스 키 입력 또는 외부 클릭 시 현재 입력 값을 서브 태그로 추가하는 로직이 잘 구현되었습니다. 빈 입력은 무시하고 유효한 값만 처리하는 검증도 적절합니다.
65-77: 키보드 이벤트 처리 함수 구현스페이스 키 입력 시 서브 태그 추가 기능과 입력 길이 검증을 처리하는 함수가 잘 구현되었습니다. 빈 입력일 경우 오류 상태를 초기화하는 로직도 적절합니다.
83-85: 서브 태그 삭제 기능 구현특정 인덱스의 서브 태그를 배열에서 제거하는 함수가 잘 구현되었습니다. 필터 함수를 사용한 방식이 간결하고 효율적입니다.
87-95: 태그 제출 로직 개선메인 태그 존재 여부와 게임 태그 검증을 통해 유효한 경우에만 제출되도록 로직이 개선되었습니다. 검증 실패 시 조기 반환하여 불필요한 처리를 방지하는 방식이 효율적입니다.
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (9)
9-9: 컨텐츠 래퍼 간격 조정컨텐츠 래퍼의 간격을 20px에서 16px로 줄여 구성 요소 간의 여백을 더 조밀하게 조정했습니다.
27-29: 서브 태그 텍스트 스타일 추가서브 태그 텍스트에 대한 스타일이 적절히 정의되었습니다. 정규 폰트와 회색 계열 색상을 사용하여 부가 정보임을 시각적으로 표현했습니다.
35-37: 오류 메시지 스타일 추가오류 메시지를 위한 스타일이 적절히 정의되었습니다. 붉은색을 사용하여 오류 상태를 명확히 표시하는 방식이 좋습니다.
43-44: 태그 래퍼 간격 조정태그 래퍼의 간격을 6px에서 8px로 늘려 구성 요소 간의 여백을 더 적절하게 조정했습니다.
46-50: 태그 하단 래퍼 스타일 추가태그 하단 부분을 위한 래퍼 스타일이 적절히 정의되었습니다. 열 방향 플렉스 레이아웃을 사용하여 요소들을 세로로 배치하는 방식이 적절합니다.
63-68: 입력 래퍼 스타일 추가입력 필드 및 오류 메시지를 위한 래퍼 스타일이 적절히 정의되었습니다. 열 방향 레이아웃과 고정 높이, 적절한 간격 설정이 UI의 일관성을 유지하는 데 도움이 됩니다.
70-79: 서브 태그 칩 스타일 추가서브 태그 칩을 위한 스타일이 잘 정의되었습니다. 둥근 모서리, 테두리, 배경색, 텍스트 색상 등이 시각적으로 일관된 태그 디자인을 만들어냅니다.
81-87: 서브 태그 버튼 스타일 추가서브 태그 삭제 버튼을 위한 스타일이 적절히 정의되었습니다. 기본 스타일을 초기화하고 필요한 속성만 설정하여 깔끔한 UI를 유지하는 방식이 좋습니다.
89-97: 동적 서브 태그 래퍼 스타일 함수 추가최대 태그 수 도달 여부에 따라 패딩을 동적으로 조정하는 스타일 함수가 잘 구현되었습니다. 플렉스 레이아웃과 랩 속성을 사용하여 여러 태그를 효과적으로 배치하는 방식이 적절합니다.
| <div css={S.tagBottomWrapper}> | ||
| <div css={S.textBox}> | ||
| <span css={S.tagTextStyling}>서브태그</span> | ||
| <span css={S.subTagTextStyling}>(최대 3개)</span> | ||
| </div> | ||
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | ||
| {currentSubTag.map((tag, idx) => ( | ||
| <div css={S.subTagChipStyling}> | ||
| <span>#{tag}</span> | ||
| <button | ||
| type="button" | ||
| css={S.subTagButtonStyling} | ||
| onClick={() => handleDeleteSubTag(idx)} | ||
| > | ||
| ⨉ | ||
| </button> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| <input | ||
| name="subTag" | ||
| css={S.inputStyling} | ||
| placeholder="ex. 너무어려운밸런스게임, 선택장애, 이상형" | ||
| maxLength={10} | ||
| value={form.subTag} | ||
| onChange={setSubTagValue} | ||
| /> | ||
| {currentSubTag.length !== 3 && ( | ||
| <div css={S.inputWrapper}> | ||
| <input | ||
| type="text" | ||
| ref={inputRef} | ||
| css={S.inputStyling} | ||
| value={inputValue} | ||
| placeholder="ex. 연애, 데이트, 데이트취향" | ||
| onChange={handleInputChange} | ||
| onKeyUp={handleKeyUp} | ||
| /> | ||
| {inputError && ( | ||
| <span css={S.errorMessageStyling}> | ||
| 서브태그 1개 당 최대 10자까지 입력 가능 | ||
| </span> | ||
| )} | ||
| </div> | ||
| )} | ||
| <Button | ||
| size="large" | ||
| variant="roundPrimary" | ||
| onClick={handleTagSubmit} | ||
| disabled={!currentMainTag} | ||
| active={!!currentMainTag} | ||
| css={S.customButtonStyle(!currentMainTag)} | ||
| > | ||
| 등록하기 | ||
| </Button> | ||
| </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.
🛠️ Refactor suggestion
서브 태그 UI 및 상호작용 개선
서브 태그 UI가 크게 개선되었습니다. 서브 태그 블록 표시, 삭제 버튼 추가, 최대 3개 제한, 입력 필드 조건부 표시, 오류 메시지 등 다양한 기능이 잘 구현되었습니다. 다만, 아래 이슈를 수정해야 합니다:
- 서브 태그 칩 컴포넌트에 고유 키가 누락되었습니다. 현재는 태그 값을 키로 사용하고 있지만, 맵 함수의 인덱스도 명시적으로 추가하는 것이 필요합니다.
- <div css={S.subTagChipStyling}>
+ <div key={`${tag}-${idx}`} css={S.subTagChipStyling}>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div css={S.tagBottomWrapper}> | |
| <div css={S.textBox}> | |
| <span css={S.tagTextStyling}>서브태그</span> | |
| <span css={S.subTagTextStyling}>(최대 3개)</span> | |
| </div> | |
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | |
| {currentSubTag.map((tag, idx) => ( | |
| <div css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| <input | |
| name="subTag" | |
| css={S.inputStyling} | |
| placeholder="ex. 너무어려운밸런스게임, 선택장애, 이상형" | |
| maxLength={10} | |
| value={form.subTag} | |
| onChange={setSubTagValue} | |
| /> | |
| {currentSubTag.length !== 3 && ( | |
| <div css={S.inputWrapper}> | |
| <input | |
| type="text" | |
| ref={inputRef} | |
| css={S.inputStyling} | |
| value={inputValue} | |
| placeholder="ex. 연애, 데이트, 데이트취향" | |
| onChange={handleInputChange} | |
| onKeyUp={handleKeyUp} | |
| /> | |
| {inputError && ( | |
| <span css={S.errorMessageStyling}> | |
| 서브태그 1개 당 최대 10자까지 입력 가능 | |
| </span> | |
| )} | |
| </div> | |
| )} | |
| <Button | |
| size="large" | |
| variant="roundPrimary" | |
| onClick={handleTagSubmit} | |
| disabled={!currentMainTag} | |
| active={!!currentMainTag} | |
| css={S.customButtonStyle(!currentMainTag)} | |
| > | |
| 등록하기 | |
| </Button> | |
| </div> | |
| <div css={S.tagBottomWrapper}> | |
| <div css={S.textBox}> | |
| <span css={S.tagTextStyling}>서브태그</span> | |
| <span css={S.subTagTextStyling}>(최대 3개)</span> | |
| </div> | |
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | |
| {currentSubTag.map((tag, idx) => ( | |
| <div key={`${tag}-${idx}`} css={S.subTagChipStyling}> | |
| <span>#{tag}</span> | |
| <button | |
| type="button" | |
| css={S.subTagButtonStyling} | |
| onClick={() => handleDeleteSubTag(idx)} | |
| > | |
| ⨉ | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| {currentSubTag.length !== 3 && ( | |
| <div css={S.inputWrapper}> | |
| <input | |
| type="text" | |
| ref={inputRef} | |
| css={S.inputStyling} | |
| value={inputValue} | |
| placeholder="ex. 연애, 데이트, 데이트취향" | |
| onChange={handleInputChange} | |
| onKeyUp={handleKeyUp} | |
| /> | |
| {inputError && ( | |
| <span css={S.errorMessageStyling}> | |
| 서브태그 1개 당 최대 10자까지 입력 가능 | |
| </span> | |
| )} | |
| </div> | |
| )} | |
| <Button | |
| size="large" | |
| variant="roundPrimary" | |
| onClick={handleTagSubmit} | |
| disabled={!currentMainTag} | |
| active={!!currentMainTag} | |
| css={S.customButtonStyle(!currentMainTag)} | |
| > | |
| 등록하기 | |
| </Button> | |
| </div> |
🧰 Tools
🪛 Biome (1.9.4)
[error] 130-131: Missing key property for this element in iterable.
The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.
(lint/correctness/useJsxKeyInIterable)
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.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (4)
39-44: useEffect 종속성 배열 최적화 필요
setCurrentSubTag가 효과 내에서 사용되지 않는데 종속성 배열에 포함되어 있습니다. 이는 불필요한 리렌더링을 유발할 수 있습니다.다음과 같이 사용하지 않는 종속성을 제거하세요:
useEffect(() => { const subTagList = inputValue ? [...currentSubTag, inputValue] : currentSubTag; setSubTagValue('subTag', subTagList.join(',')); - }, [currentSubTag, setCurrentSubTag, inputValue, setSubTagValue]); + }, [currentSubTag, inputValue, setSubTagValue]);
55-76: 스페이스바 및 키보드 이벤트 처리 로직 개선 가능현재 구현은 기능적으로 문제가 없지만, 몇 가지 개선할 수 있는 부분이 있습니다:
e.preventDefault()는keyUp이벤트보다keyDown이벤트에서 더 효과적입니다.- 빈 문자열이나 공백만 있는 태그를 방지하는 검증이 있지만, 최소 길이 검증은 없습니다.
- 중복 태그 검증이 없습니다.
다음과 같이 개선할 수 있습니다:
const handleSpaceAction = () => { if (!inputValue.trim()) return; + + // 중복 태그 검증 + if (currentSubTag.includes(inputValue.trim())) { + setInputError(true); + return; + } setCurrentSubTag((prev) => [...prev, inputValue.trim()]); setInputValue(''); setInputError(false); }; useOutsideClick(inputRef, handleSpaceAction); - const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => { + const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { if (!inputValue) { setInputError(false); return; } if (e.code === 'Space') { e.preventDefault(); handleSpaceAction(); } setInputError(inputValue.length >= 10); };그리고 입력 필드의 이벤트 핸들러를 업데이트하세요:
<input type="text" ref={inputRef} css={S.inputStyling} value={inputValue} placeholder="ex. 연애, 데이트, 데이트취향" onChange={handleInputChange} - onKeyUp={handleKeyUp} + onKeyDown={handleKeyDown} />
86-94: 폼 제출 시 사용자 피드백 개선 필요현재 구현에서는 유효성 검사 실패 시 특별한 사용자 피드백이 없습니다. 이는 사용자 경험을 저하시킬 수 있습니다.
다음과 같이 개선할 수 있습니다:
const handleTagSubmit = () => { if (!currentMainTag) return; const { isValid } = validateGameTag(form); - if (!isValid) return; + if (!isValid) { + // 오류 알림 표시 또는 상태 업데이트 + alert('태그 정보가 유효하지 않습니다. 다시 확인해 주세요.'); + return; + } submitGame(); onClose?.(); };
158-167: 버튼 스타일 및 접근성 개선 가능등록하기 버튼의 기능과 스타일은 잘 구현되었지만, 접근성 측면에서 개선할 수 있는 부분이 있습니다.
ARIA 속성을 추가하여 스크린 리더 사용자를 위한 접근성을 향상시키세요:
<Button size="large" variant="roundPrimary" onClick={handleTagSubmit} disabled={!currentMainTag} active={!!currentMainTag} css={S.customButtonStyle(!currentMainTag)} + aria-label="밸런스게임 태그 등록하기" > 등록하기 </Button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build
🔇 Additional comments (8)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (8)
8-10: 새 기능 추가를 위한 효율적인 가져오기 추가필요한 유틸리티와 훅을 적절하게 가져왔습니다. 특히
createArrayFromCommaString은 콤마로 구분된 문자열을 배열로 변환하는 작업을 분리하여 재사용성을 높입니다.
18-18: 프롭스 타입 시그니처가 적절하게 업데이트됨이벤트 핸들러 대신 직접적인 값을 받는 방식으로 변경되어 더 명확한 인터페이스를 제공합니다.
30-35: 상태 관리 로직 개선
inputRef를 추가하여 외부 클릭 감지를 위한 참조 포인트를 만들었습니다.createArrayFromCommaString유틸을 사용해 폼 데이터를 초기 배열로 적절히 변환했습니다.- 서브 태그를 배열로 관리하는 방식으로 변경하여 데이터 처리가 일관성 있게 개선되었습니다.
36-38: 입력 상태 및 오류 관리 상태 추가사용자 입력값과 오류 상태를 별도로 관리하여 UI/UX가 개선되었습니다.
46-53: 입력 검증 로직이 적절히 구현됨사용자가 10자 이상 입력하지 못하도록 제한하고, 입력값 변경 시 오류 상태를 초기화하는 로직이 잘 구현되었습니다.
82-84: 삭제 기능이 적절히 구현됨인덱스를 기반으로 특정 태그를 삭제하는 기능이 잘 구현되었습니다.
121-139: 서브태그 UI 및 키 관리 개선이전 리뷰에서 지적된 키 프롭 문제가 해결되었습니다. 각 태그 아이템에 고유한 키가 할당되어 React의 재조정 프로세스가 효율적으로, 애니메이션이나 포커스 상태를 유지하는 데 도움이 됩니다.
하지만 태그 값이 중복될 경우 키도 중복될 수 있으니 주의가 필요합니다.
태그 값이 중복되면 키도 중복되어 React 경고가 발생할 수 있습니다. 현재 로직에서 중복 태그를 방지하는지 확인해보세요.
140-157: 조건부 렌더링 및 오류 메시지 구현이 잘되었음태그가 3개일 때 입력 필드를 숨기는 조건부 렌더링과 오류 메시지 표시 로직이 적절하게 구현되었습니다. 사용자 경험이 향상되었습니다.
WonJuneKim
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.
제가 수정중인 방식과 거의 비슷하네요..! 저도 서둘러서 웹 쪽 서브태그 올리도록 하겠습니다! 고생 많으셨어요!!
| form.games[gameStage].gameOptions[selectedOptionId].fileId ?? | ||
| null, |
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.
널 병합 연산자 사용 좋습니다!
| setSubTagValue, | ||
| submitGame, | ||
| }: GameTagModalProps) => { | ||
| const inputRef = useRef<HTMLInputElement>(null); |
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.
의 ref를 훅과 적절하게 연결되어 있는거 같네요~
alwubin
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.
역시 수정도 완벽한...🤩남긴 코멘트 확인 부탁드리며, 구현하시느라 수고 많으셨습니다ㅎㅎ👍👏
| ? [...currentSubTag, inputValue] | ||
| : currentSubTag; | ||
| setSubTagValue('subTag', subTagList.join(',')); | ||
| }, [currentSubTag, setCurrentSubTag, inputValue, setSubTagValue]); |
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.
setCurrentSubTag는 고정된 참조를 가지고 있는거처럼 보이는데 맞다면 의존성 배열에 포함될 필요 없지 않을까요?
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.
코드를 작성하다가 필요없는 부분을 남겨놨네요..ㅎㅎ
722cb1a 에서 삭제했습니다!
|
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.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (2)
46-53: 10자 제한 로직 재검토입력 제한(10자)은 뷰 단에서만 처리하고 있습니다. 후속 서버 검증 로직이 없으면 안전하지 않을 수 있으니, 서버 측에서도 문자열 길이를 검증하는 방안을 고려해주세요.
151-155: 에러 메시지 접근성에러 메시지(
서브태그 1개 당 최대 10자까지 입력 가능)가 시각적 안내 외에 스크린 리더 사용자에게도 잘 전달되는지 확인이 필요합니다.aria-live속성 등을 추가해 보세요.src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (1)
81-88:subTagButtonStyling버튼 스타일태그 삭제 버튼에
all: 'unset'을 사용하여 기본 버튼 스타일을 제거하고, 최소한의 색상만 지정해 시인성이 좋아졌습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts(3 hunks)src/components/mobile/molecules/GameTagModal/GameTagModal.tsx(3 hunks)src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx(1 hunks)src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts(1 hunks)src/styles/color.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/styles/color.ts
- src/components/mobile/organisms/BalanceGameCreateSection/BalanceGameCreateSection.tsx
- src/components/mobile/organisms/BalanceGameSection/BalanceGameSection.style.ts
🔇 Additional comments (15)
src/components/mobile/molecules/GameTagModal/GameTagModal.tsx (7)
1-1: React 관련 import 정상 확인
useRef,useState,useEffect각각 적절히 사용되고 있으며, 특별한 문제는 없어 보입니다.
9-10: 유틸 및 커스텀 훅 가져오기
createArrayFromCommaString와useOutsideClick을 통해 서브태그 배열 생성과 외부 클릭 처리 로직이 명확히 분리되었습니다. 재사용성 면에서 좋아 보입니다.
18-18: 매개변수 기반으로 서브태그 설정
setSubTagValue함수를(name, tag)형태로 변경하여 이벤트 객체가 아닌 명시적 인자를 사용하는 방식은 의도를 더욱 명확히 해줍니다.
33-35: 서브 태그 배열 초기화 시 null/빈 문자열 처리 주의
createArrayFromCommaString(form.subTag)에서form.subTag가null또는 빈 문자열일 가능성이 있다면, 배열이 올바르게 초기화되는지 확인이 필요합니다.
39-45:useEffect에서 3개 제한 처리 여부 확인현재
inputValue가 남아있는 상태에서 이미currentSubTag가 3개 이상이면 새로운 태그가 추가되지 않아야 할 수도 있습니다. 의도된 동작인지 확인해주세요.
64-76: Space 이외의 다른 키 입력 처리
handleKeyUp에서 스페이스만 태그로 변환하고 있습니다. 엔터 키 등 다른 입력 패턴 대응이 필요한지 기획 의도를 다시 한번 점검해보세요.
158-167: 등록 시 서브태그 없는 경우현재
disabled조건이 메인 태그만 검사합니다. 서브태그가 없어도 등록 가능한지 기획 요구사항을 확인해주세요. 필요하다면 서브태그 최소 개수 제한도 반영하는 방안을 고려해주세요.src/components/mobile/molecules/GameTagModal/GameTagModal.style.ts (8)
9-9: 간격 조정 확인
gap: '16px'으로 줄어 불필요한 여백이 줄었을 것으로 보이며, 전체적인 UI 밀도가 좋아졌을 것으로 예상됩니다.
27-29:subTagTextStyling추가서브태그 관련 안내 문구 스타일이 별도로 정의되어 있어, 텍스트와 기타 스타일 요소를 분리하는 관점에서 명확합니다.
35-38:errorMessageStyling정의오류 메시지 스타일이 별도로 분리되어 있어 재사용성과 코드 가독성이 높아졌습니다.
43-44: 태그 래퍼의gap증가
gap: '8px'으로 변경됨에 따라 태그들이 서로 겹치지 않고 간격을 충분히 확보할 수 있을 것으로 보입니다.
46-50:tagBottomWrapper스타일 추가서브태그 영역과 하단 버튼을 모두 래핑하는 컨테이너로,
width: '100%'와 세로 플렉스 구조가 합리적으로 보입니다.
63-69:inputWrapper로 필드와 에러 메시지 구분에러 메시지를 필드 아래에 깔끔하게 배치하기 위한 스타일로 보입니다. 사용성 및 가독성이 향상될 것입니다.
70-80:subTagChipStyling세부 스타일태그 칩 요소를 시각적으로 구분하기 위한 색상, 테두리, 배경 스타일이 명확하게 지정되었습니다. 태그 UI로서 역할을 잘 수행할 것으로 보입니다.
89-98:subTagWrapper동적 패딩 처리
isTagMax를 사용하여 태그가 3개일 때와 아닐 때 패딩을 다르게 주는 로직이 명확합니다. UI 요구사항에 부합하도록 잘 설계된 것으로 보입니다.
| </div> | ||
| <div css={S.subTagWrapper(currentSubTag.length === 3)}> | ||
| {currentSubTag.map((tag, idx) => ( | ||
| <div css={S.subTagChipStyling} key={tag}> |
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.
🛠️ Refactor suggestion
key 값으로 중복 태그 식별 문제
현재 key={tag}를 사용하면 같은 문자열 태그가 여러 번 입력될 경우 React에서 중복 키 충돌이 일어날 수 있습니다. 안정적인 업데이트를 위해 key={${tag}-${idx}}처럼 인덱스를 함께 포함시키는 것을 권장합니다.
| if (!inputValue.trim()) return; | ||
|
|
||
| setCurrentSubTag((prev) => [...prev, inputValue.trim()]); | ||
| setInputValue(''); | ||
| setInputError(false); | ||
| }; | ||
| useOutsideClick(inputRef, handleSpaceAction); | ||
|
|
||
| const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => { |
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.
🛠️ Refactor suggestion
handleSpaceAction에 최대 태그 수 제한 추가 권장
사용자가 이미 3개의 태그를 만든 뒤 입력을 유지한 상태에서 밖을 클릭하면 handleSpaceAction을 통해 추가 태그가 생성될 수도 있습니다. 아래와 같이 간단한 조건을 추가하는 것을 제안드립니다:
if (!inputValue.trim()) return;
+ if (currentSubTag.length >= 3) return;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSpaceAction = () => { | |
| if (!inputValue.trim()) return; | |
| setCurrentSubTag((prev) => [...prev, inputValue.trim()]); | |
| setInputValue(''); | |
| setInputError(false); | |
| }; | |
| useOutsideClick(inputRef, handleSpaceAction); | |
| const handleSpaceAction = () => { | |
| if (!inputValue.trim()) return; | |
| if (currentSubTag.length >= 3) return; | |
| setCurrentSubTag((prev) => [...prev, inputValue.trim()]); | |
| setInputValue(''); | |
| setInputError(false); | |
| }; | |
| useOutsideClick(inputRef, handleSpaceAction); |
WonJuneKim
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.
추가 수정사항 까지 모두 확인했습니다! 수고 많으셨습니다!!! ㅎㅎㅎ



💡 작업 내용
💡 자세한 설명
✅ GameTagModal
서브 태그 관련 로직은 아래와 같습니다!
const [subTagArray] = useState(() => createArrayFromCommaString(form.subTag));와 같이 배열로 만들어 사용합니다.useEffect를 사용하여 인풋에 텍스트가 존재할 경우엔 블록 처리된 서브 태그 배열인currentSubTag에inputValue을 추가한 값을 최종 서브 태그 배열로 처리되게 하였습니다!join함수를 사용하여 하나의 문자열로 묶이도록 하였습니다!2025-03-03.165132.mp4
📗 참고 자료 (선택)
📢 리뷰 요구 사항 (선택)
🚩 후속 작업 (선택)
밸런스 게임 모바일 수정 pr이 먼저 머지되면 수정 시에도 서브 태그 관련 로직 정상 작동하도록 연결 후 머지하도록 하겠습니다!
✅ 셀프 체크리스트
closes #308
Summary by CodeRabbit