-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] 설정API 연동 #95
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
Changes from all commits
90145c2
1ff3cf9
4e9367e
2eb294e
dd9913d
f73b5ee
27301ba
a34414f
7d44260
8971e88
27b7d52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { TChangeNicknamePayload, TChangeNicknameResponse, TChangePasswordPayload, TGetMemberGradeResponse, TMemberInfo } from '@/types/auth/account'; | ||
| import type { TCommonResponse } from '@/types/common/common'; | ||
|
|
||
| import { axiosInstance } from '@/api/axiosInstance'; | ||
|
|
||
| // 비밀번호 변경 | ||
| export async function changePassword(payload: TChangePasswordPayload): Promise<void> { | ||
| const body = { nowPassword: payload.currentPassword, newPassword: payload.newPassword }; | ||
| await axiosInstance.patch('/api/v1/members/passwords', body); | ||
| } | ||
|
|
||
| // 닉네임 변경 | ||
| export async function changeNickname(payload: TChangeNicknamePayload): Promise<TChangeNicknameResponse> { | ||
| const { data } = await axiosInstance.patch<TChangeNicknameResponse>('/api/v1/members/infos', payload); | ||
| return data; | ||
| } | ||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // 탈퇴 | ||
| export async function deleteMember(): Promise<void> { | ||
| await axiosInstance.delete('/api/v1/members'); | ||
| } | ||
|
|
||
| // 사용자 정보 조회 | ||
| export async function getMemberInfo(): Promise<TCommonResponse<TMemberInfo>> { | ||
| const { data } = await axiosInstance.get<TCommonResponse<TMemberInfo>>('/api/v1/members/infos'); | ||
| return data; | ||
| } | ||
|
|
||
| // 사용자 등급 조회 | ||
| export async function getMemberGrade(): Promise<TGetMemberGradeResponse> { | ||
| const { data } = await axiosInstance.get<TGetMemberGradeResponse>('/api/v1/members/grade'); | ||
| return data; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import type { TResetPreferencesResponse } from '@/types/dates/preferences'; | ||
|
|
||
| import { axiosInstance } from '../axiosInstance'; | ||
|
|
||
| // 취향 데이터 초기화 | ||
| export async function resetPreferences(): Promise<TResetPreferencesResponse> { | ||
| const { data } = await axiosInstance.delete<TResetPreferencesResponse>('/api/v1/dates/preferences'); | ||
| return data; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export const faqKeys = { | ||
| all: ['faqs'] as const, | ||
| list: (p: { category: string; page: number; size: number }) => [...faqKeys.all, 'list', p] as const, | ||
| search: (p: { keyword: string; category?: string; page: number; size: number }) => [...faqKeys.all, 'search', p] as const, | ||
| }; | ||
|
Comment on lines
+1
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 키 빌더 패턴 적절합니다. 파라미터 타입 재사용을 검토해 주세요 구조/리터럴 타입 보존(as const)도 적절합니다. 다만 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import type { TFaqCategory, TFetchFaqsResponse } from '@/types/faq/faq'; | ||
|
|
||
| import { axiosInstance } from '@/api/axiosInstance'; | ||
|
|
||
| // FAQ 목록 조회 | ||
| export const getFaqs = async (params: { category: TFaqCategory; page: number; size: number }) => { | ||
| const { data } = await axiosInstance.get<TFetchFaqsResponse>('/api/v1/faqs', { | ||
| params: { | ||
| faqCategory: params.category, | ||
| page: params.page, | ||
| size: params.size, | ||
| }, | ||
| }); | ||
| return data; | ||
| }; | ||
|
|
||
| // FAQ 검색 | ||
| export const searchFaqs = async (params: { keyword: string; category?: TFaqCategory; page: number; size: number }) => { | ||
| const { data } = await axiosInstance.get<TFetchFaqsResponse>('/api/v1/faqs/search', { | ||
| params: { | ||
| keyword: params.keyword, | ||
| faqCategory: params.category, | ||
| page: params.page, | ||
| size: params.size, | ||
| }, | ||
| }); | ||
| return data; | ||
| }; | ||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| export const noticeKeys = { | ||
| root: ['notice'] as const, // 루트 키 | ||
|
|
||
| // 목록 조회 키 | ||
| list: (p: { category: 'SERVICE' | 'SYSTEM'; page: number; size: number }) => [...noticeKeys.root, 'list', p] as const, | ||
|
|
||
| // 상세 조회 키 | ||
| detail: (id: number) => [...noticeKeys.root, 'detail', id] as const, | ||
|
|
||
| // 검색 조회 키 | ||
| search: (p: { keyword: string; page: number; size: number; category?: 'SERVICE' | 'SYSTEM' }) => [...noticeKeys.root, 'search', p] as const, | ||
| }; | ||
|
Comment on lines
+1
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainNotice 키 정의가 두 곳으로 분산되어 있습니다 — 단일 소스로 통합 권장 relevant_code_snippets에 따르면 기존에도 NoticeKeys(getAllNotices 등)가 존재합니다. 키 스키마가 중복되면 캐시 충돌/이중 관리가 생깁니다. notice.keys.ts를 단일 소스로 승격하고 기존 키는 deprecated 처리 또는 래핑으로 일원화해 주세요. 레거시 키 사용처를 빠르게 확인하려면: 🏁 Script executed: #!/bin/bash
# 레거시 NoticeKeys 사용처 탐색
rg -n -A 2 "NoticeKeys\."
# 신규 noticeKeys 사용처 탐색
rg -n -A 2 "noticeKeys\."Length of output: 1895 🏁 Script executed: #!/bin/bash
# 레거시 NoticeKeys 정의 위치 탐색
rg -n "export const NoticeKeys" -C 2
rg -n "NoticeKeys\s*=\s*{" -C 2Length of output: 527 🏁 Script executed: #!/bin/bash
# NoticeKeys 전체 정의 블록 확인
rg -n "export const NoticeKeys" -A 20 src/queryKey/queryKey.tsLength of output: 290 Notice 키 정의 단일화 필요 영향 받는 위치:
예시(diff 제안): // src/queryKey/queryKey.ts
-export const NoticeKeys = createQueryKeys('notice', {
- all: () => ['notice'],
- getAllNotices: (page, size, noticeCategory) => ['notice', page, size, noticeCategory],
-});
+export const noticeKeys = createQueryKeys('notice', {
+ root: () => ['notice'],
+ list: (p: { category: 'SERVICE' | 'SYSTEM'; page: number; size: number }) =>
+ [...root(), 'list', p] as const,
+ detail: (id: number) =>
+ [...root(), 'detail', id] as const,
+ search: (p: { keyword: string; page: number; size: number; category?: 'SERVICE' | 'SYSTEM' }) =>
+ [...root(), 'search', p] as const,
+});
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,36 @@ | ||
| import type { TFetchNoticeDetailResponse, TFetchNoticesResponse, TRequestGetNoticeRequest } from '@/types/notice/notice'; | ||
| import type { TFetchNoticeDetailResponse, TFetchNoticesResponse } from '@/types/notice/notice'; | ||
|
|
||
| import { axiosInstance } from '@/api/axiosInstance'; | ||
|
|
||
| // 공지사항 전체 조회 API | ||
| export const fetchNotices = async ({ noticeCategory = 'SERVICE', page, size }: TRequestGetNoticeRequest): Promise<TFetchNoticesResponse> => { | ||
| const { data } = await axiosInstance.get('/api/v1/notices', { | ||
| params: { noticeCategory: noticeCategory, page, size }, | ||
| // 공지사항 전체 조회 | ||
| export const getNotices = async (params: { category: 'SERVICE' | 'SYSTEM'; page: number; size: number }) => { | ||
| const { data } = await axiosInstance.get<TFetchNoticesResponse>('/api/v1/notices', { | ||
| params: { | ||
| noticeCategory: params.category, | ||
| page: params.page, | ||
| size: params.size, | ||
| }, | ||
| }); | ||
| return data; | ||
| }; | ||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // 공지사항 상세 조회 API | ||
| export const fetchNoticeDetail = async (noticeId: number): Promise<TFetchNoticeDetailResponse> => { | ||
| const { data } = await axiosInstance.get(`/api/v1/notices/${noticeId}`); | ||
| // 상세 조회 | ||
| export const getNoticeDetail = async (noticeId: number) => { | ||
| const { data } = await axiosInstance.get<TFetchNoticeDetailResponse>(`/api/v1/notices/${noticeId}`); | ||
| return data; | ||
| }; | ||
|
|
||
| // 공지 검색 | ||
| export const searchNotices = async (params: { keyword: string; page: number; size: number; category?: 'SERVICE' | 'SYSTEM' }) => { | ||
| const { keyword, page, size, category } = params; | ||
|
|
||
| const { data } = await axiosInstance.get<TFetchNoticesResponse>('/api/v1/notices/search', { | ||
| params: { | ||
| keyword, | ||
| page, | ||
| size, | ||
| ...(category && { noticeCategory: category }), | ||
| }, | ||
| }); | ||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return data; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import type { TAlarmSettings, TGetAlarmSettingsResp, TPatchAlarmSettingsResp } from '@/types/settingAlarm/alarm'; | ||
|
|
||
| import { axiosInstance } from '@/api/axiosInstance'; | ||
|
|
||
| // 조회 | ||
| export async function getAlarmSettings(): Promise<TGetAlarmSettingsResp> { | ||
| const { data } = await axiosInstance.get<TGetAlarmSettingsResp>('/api/v1/alarms/settings'); | ||
| return data; | ||
| } | ||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // 업데이트 | ||
| export async function patchAlarmSettings(payload: TAlarmSettings): Promise<TPatchAlarmSettingsResp> { | ||
| const { data } = await axiosInstance.patch<TPatchAlarmSettingsResp>('/api/v1/alarms/settings', payload); | ||
| return data; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| //setting - common input box | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useEffect, useRef, useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import SearchIcon from '@/assets/icons/Search_Blank.svg?react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,10 +14,14 @@ interface IEditableInputBoxProps { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| onSearchClick?: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| onEditStart?: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFocus?: React.FocusEventHandler<HTMLInputElement | HTMLTextAreaElement>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function EditableInputBox({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mode = 'default', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| label = '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,12 +31,29 @@ export default function EditableInputBox({ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| onSearchClick, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className = '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder = '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly = false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onEditStart, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFocus, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: IEditableInputBoxProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isEditing, setIsEditing] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isNickname = mode === 'nickname'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isSearch = mode === 'search'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 편집 모드 - 자동 포커스 + 전체 선택 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const el = inputRef.current; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isNickname && isEditing && el) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const frameId = requestAnimationFrame(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.focus(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (el as HTMLInputElement | HTMLTextAreaElement).select(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => cancelAnimationFrame(frameId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isNickname, isEditing]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleCancel = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsEditing(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onCancel?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -69,48 +89,62 @@ export default function EditableInputBox({ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| {label && <p className="font-body1 text-default-gray-700 mb-1">{label}</p>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="relative w-full"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 닉네임 - 수정 중일 때 textarea */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 닉네임 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isNickname && isEditing ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <textarea value={value} onChange={onChange} placeholder={placeholder} maxLength={maxLength} className={`${sharedClassName} resize-none`} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <textarea | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref={inputRef as React.RefObject<HTMLTextAreaElement>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={value} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={onChange} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder={placeholder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxLength={maxLength} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={`${sharedClassName} resize-none`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFocus={onFocus} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+94
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. readOnly=true일 때 닉네임 편집이 막히지 않습니다 textarea에는 readOnly가 전달되지 않아 편집이 가능해집니다. 읽기 전용 모드에서 편집 진입 자체도 차단하는 것이 일관됩니다. {isNickname && isEditing ? (
<textarea
ref={inputRef as React.RefObject<HTMLTextAreaElement>}
value={value}
onChange={onChange}
placeholder={placeholder}
maxLength={maxLength}
+ readOnly={readOnly}
className={`${sharedClassName} resize-none`}
onFocus={onFocus}
/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="text" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref={inputRef as React.RefObject<HTMLInputElement>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type={type ?? 'text'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={value} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={onChange} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly={isNickname ? !isEditing : false} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| readOnly={readOnly || (isNickname ? !isEditing : false)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder={placeholder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxLength={maxLength} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onKeyDown={handleKeyDown} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={sharedClassName} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFocus={onFocus} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 수정 버튼 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 수정 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isNickname && !isEditing && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => setIsEditing(true)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsEditing(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| onEditStart?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Seojegyeong marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="absolute right-3 top-1/2 -translate-y-1/2 font-body1 px-3 py-1 rounded-full bg-default-gray-400 text-default-gray-700" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+118
to
126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 읽기 전용 모드에서 '수정' 버튼 비활성화/가드 추가 readOnly=true에서도 수정 버튼이 동작합니다. 비활성화와 클릭 가드를 함께 적용해 주세요. {isNickname && !isEditing && (
<button
- type="button"
- onClick={() => {
- setIsEditing(true);
- onEditStart?.();
- }}
+ type="button"
+ disabled={readOnly}
+ onClick={() => {
+ if (readOnly) return;
+ setIsEditing(true);
+ onEditStart?.();
+ }}
className="absolute right-3 top-1/2 -translate-y-1/2 font-body1 px-3 py-1 rounded-full bg-default-gray-400 text-default-gray-700"
>
수정
</button>
)}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 수정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 글자 수 표시 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 글자 수 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isNickname && isEditing && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="absolute bottom-2 right-4 font-body1 text-default-gray-500"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {value.length} / {maxLength} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 검색 버튼 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 검색 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isSearch && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button type="button" onClick={onSearchClick} className="absolute right-3 top-1/2 -translate-y-1/2 p-1"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <SearchIcon className="w-5 h-5 text-primary-500" stroke="currentColor" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
140
to
144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 검색 버튼에 접근성 라벨 추가 권장 아이콘 버튼은 스크린리더에 의미가 전달되지 않습니다. aria-label을 추가해 주세요. - {isSearch && (
- <button type="button" onClick={onSearchClick} className="absolute right-3 top-1/2 -translate-y-1/2 p-1">
+ {isSearch && (
+ <button type="button" aria-label="검색 실행" onClick={onSearchClick} className="absolute right-3 top-1/2 -translate-y-1/2 p-1">
<SearchIcon className="w-5 h-5 text-primary-500" stroke="currentColor" />
</button>
)}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 취소, 완료 버튼 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* 취소, 완료 */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| {isNickname && isEditing && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="flex justify-end gap-2 mt-3"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| <button onClick={handleCancel} className="font-body1 px-4 py-1.5 rounded-full bg-default-gray-400 text-default-gray-700"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.