From d2694f7bba780f754ef648b5986b710c61e4a575 Mon Sep 17 00:00:00 2001 From: Junsub Lee Date: Wed, 14 May 2025 21:29:31 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20JobCategories=20ENUM=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ringus.pem | 27 ---- src/auth/components/signup/UsagePolicy.tsx | 123 +++++++++++------ src/content/terms-of-service.txt | 59 ++++++++ src/global/components/JobCategories.tsx | 84 +----------- src/mentorship/pages/MentorInfoView.tsx | 108 +++++---------- src/user/components/Mapping.ts | 63 ++------- .../profile/mentor/MentorFields.tsx | 28 ++-- .../profile/mentor/MentorProfile1.tsx | 126 +++++++----------- 8 files changed, 241 insertions(+), 377 deletions(-) delete mode 100644 ringus.pem create mode 100644 src/content/terms-of-service.txt diff --git a/ringus.pem b/ringus.pem deleted file mode 100644 index 84a5f54..0000000 --- a/ringus.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEArZaqVLRApg80VuUUiOwP3NJiFgxsu0TNgR4aJ6St7YR7mkSQ -hkDBCcJIVSBfqkNDtyyuQci7JrFuAyN+m5zHkbv0X0oFKB8pFKWCGEeUn9UWJXDb -3OAp7MamsEjUhSds9SD8vtHGRhA3tWq8kbUD60YpMpfoTj0EviZj/Ik+LaSshUDE -ryazI6+xIa3uhLNmn+4O2uizu28QAgl2Iq3MYAFhrNrjIYAgRtSh+CAS7lqvtW2d -oDqeWUqh1VE+k+jlYMCB1x5lasOW8VBKA1jUm8t265jBrVKL8lB7Dk2LltMpavg3 -YI3UzkXGtrS7AapEf98RTdITbtHLGwZRSFSzzQIDAQABAoIBADImkzNBmGPhhKeO -K26bdMHBbmEcWdWIvS6OedP2OeGjIuqg4HhJAPxGywr8/WZ9ZHTpTbbnvVoibLwv -ZwaiNu4dtS5Kfk7nIcE7R+in1YKP14QdpQedI7+qbMIFaJDHoSz4yMyAYp4fVVju -a2hVObqhXImnZZAlBNfC06REKliY/sbWwzzb2NuhjCG40AROIqVVeNfwuIJZtmFC -h3X2OVdI7DWNn+Fix97n08JAjvx3ov/BSvG3aJpx8ZhO/hPO1yVca4PHpv6Orb43 -pXzk/dm0QC9NwweWMBZ0ZwdWvU1gsPIfNnvVGoz4qKc0QYYcLYRQibFe5/ZnGGF7 -CGrEz+kCgYEA4OV1VPRquveWCeW1zyX5vOutQl0qm3BYL9oNr5DMcQOM0k5bIUlJ -jHu/M70vYetsgYLfuDXdPb7xqIc9QaX94q39vYZZT+3lxfcgJIPk1Jq5o4XqX5+z -LYmGtDFfA6oBEcTUe//AWU9Mh47/ATmvheoI4JMK5D6oeU4xUS4zvbMCgYEAxZii -jIiVw46UQwCFCc2Qw3tNc6QzgIzS4keAWXOZKoEQz79/mkT9zPZ9NMQykwkrI4Y+ -QARqDi7Z2iZmijWXk0IjwI3XIxYx8BALsuO3Pd2CSsN2DS3roTFR4fY+3KvCZ2/W -p/LG9dRvUGjJNm/GV+4Lb0AVZZPZL4EEIoBUCH8CgYEA3FURycSYGErebR3nLGZp -MQS4vy7lwlmjnGYGSH4VPZebzKLFt2vEqeTG41qy5D0xFgVxR1lGQusieNjeU8Xb -YczSrm7Ea4GIPpYpoHyzoPNhcmqNv1eHxNJa7Yj9LGrPF2h+QGnFOfpt4NVg0gOB -CjLKtbJ51jno9sd7m6wnNu8CgYAKJUUrXBP8f4SjUBKEp1Zogxs99c9jKVfmoG85 -qJLTuN7JG/cMT8CMVpelLvG91PmvEER/+voLEmLDLbeUHx5SRFIbn+zM82XLArfn -DoQpHAeFmTWlhZcUpriilocxw2vu7bIi23dVxfuVMFwsfF69ww45PxwaJBZBtykk -MFygNwKBgQDbRMJH6IcbOF8HWa2VQp10VnnJaw7P1zYvuIXDgHvizePxNj2OD0zS -hC8tnCBTyhUPSRPs3QIN5PQmzMfFT1HzzQMGqb5TKL4WXR8VO2YNcsvnx60sigMD -MK3GcLuMjKSPZcNNY5x81fLPSxbj2ZTmkyXqp5fIoQ9OxSZ113Litg== ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/src/auth/components/signup/UsagePolicy.tsx b/src/auth/components/signup/UsagePolicy.tsx index 5b6c81d..bd0123f 100644 --- a/src/auth/components/signup/UsagePolicy.tsx +++ b/src/auth/components/signup/UsagePolicy.tsx @@ -15,50 +15,66 @@ const UsagePolicy = ({ marketing: false, }); - // 모든 필수 항목이 체크되었는지 확인 - const isAllRequiredChecked = checkedItems.terms && checkedItems.privacy; + // 모달 열기용 상태 + const [modalType, setModalType] = useState<'terms' | 'privacy' | null>(null); - // 모든 항목이 체크되었는지 확인 + const isAllRequiredChecked = checkedItems.terms && checkedItems.privacy; const allChecked = checkedItems.terms && checkedItems.privacy && checkedItems.marketing; - // 개별 체크박스 변경 핸들러 const handleSingleCheck = (name: string) => { - setCheckedItems((prev: any) => { - const newState = { ...prev, [name]: !prev[name] }; - return newState; - }); + setCheckedItems((prev: any) => ({ ...prev, [name]: !prev[name] })); }; - // ✅ `useEffect`에서 상태 변경을 감지한 후 `onAgree` 호출 useEffect(() => { onAgree('TERMS_OF_SERVICE', checkedItems.terms); onAgree('PRIVACY_POLICY', checkedItems.privacy); onAgree('MARKETING_CONSENT', checkedItems.marketing); - }, [checkedItems]); // checkedItems 변경 시 실행 + }, [checkedItems]); - // "모두 동의" 체크박스 핸들러 const handleAllCheck = () => { - const newCheckedState = !allChecked; - const updatedState = { - terms: newCheckedState, - privacy: newCheckedState, - marketing: newCheckedState, - }; - - setCheckedItems(updatedState); - - // `onAgree` 호출하여 백엔드에 전달될 데이터 업데이트 - onAgree('TERMS_OF_SERVICE', newCheckedState); - onAgree('PRIVACY_POLICY', newCheckedState); - onAgree('MARKETING_CONSENT', newCheckedState); + const newState = !allChecked; + setCheckedItems({ + terms: newState, + privacy: newState, + marketing: newState, + }); + onAgree('TERMS_OF_SERVICE', newState); + onAgree('PRIVACY_POLICY', newState); + onAgree('MARKETING_CONSENT', newState); + console.log('모두 동의:', newState); + }; - // 콘솔에서 확인 (현재 상태 출력) - console.log('"모두 동의" 선택됨:', [ - { tag: 'TERMS_OF_SERVICE', agreed: newCheckedState }, - { tag: 'PRIVACY_POLICY', agreed: newCheckedState }, - { tag: 'MARKETING_CONSENT', agreed: newCheckedState }, - ]); + // 상세보기 모달 열린 경우 렌더 + const renderModal = () => { + if (!modalType) return null; + return ( +
+
+

+ {modalType === 'terms' + ? '링어스 이용약관' + : '개인정보 수집 및 이용 동의'} +

+
+ {/* 실제 약관 텍스트를 여기 로드하세요 */} +

+ 여기에{' '} + {modalType === 'terms' + ? '서비스 이용 약관...' + : '개인정보 처리 방침...'}{' '} + 내용을 표시합니다. +

+
+ +
+
+ ); }; return ( @@ -82,19 +98,38 @@ const UsagePolicy = ({
- {/* 개별 약관 동의 */} - handleSingleCheck('terms')} - /> - handleSingleCheck('privacy')} - /> + {/* 개별 약관 동의 + 상세보기 */} +
+ handleSingleCheck('terms')} + /> + +
+ +
+ handleSingleCheck('privacy')} + /> + +
+ + {/* 선택 약관 */} + + {renderModal()} ); }; diff --git a/src/content/terms-of-service.txt b/src/content/terms-of-service.txt new file mode 100644 index 0000000..47a1c26 --- /dev/null +++ b/src/content/terms-of-service.txt @@ -0,0 +1,59 @@ +[이용약관] + +제 1 조 (목적) + +본 약관은 RingUs(이하 "서비스")가 제공하는 멘토링 플랫폼의 이용과 관련하여, 회사와 이용자의 권리, 의무 및 책임 사항을 규정하는 것을 목적으로 합니다. + + + +제 2 조 (정의) + +"RingUs"란 대학생과 같은 대학 출신 현직자 선배를 연결하여 1:1 멘토링을 제공하는 온라인 플랫폼을 의미합니다. + +"회원"이란 본 약관에 동의하고 서비스에 가입한 자를 의미합니다. + +"멘토"란 자신의 경험과 지식을 바탕으로 멘토링을 제공하는 회원을 의미합니다. + +"멘티"란 멘토링을 신청하여 멘토의 도움을 받는 회원을 의미합니다. + + + +제 3 조 (회원가입 및 계정 관리) + +회원은 본인의 정확한 정보를 제공하여야 하며, 잘못된 정보 제공으로 인한 불이익은 회원 본인의 책임입니다. + +학교 인증 절차를 완료해야 가입할 수 있으며, 인증되지 않은 경우 서비스 이용이 제한될 수 있습니다. + +계정의 관리 책임은 회원 본인에게 있으며, 계정 도용으로 발생한 문제에 대해 회사는 책임을 지지 않습니다. + + + +제 4 조 (서비스 이용) + +회원은 서비스 이용 시 관련 법령 및 본 약관을 준수해야 합니다. + +회원은 허위 정보 제공, 타인의 계정 도용, 부적절한 언행 등을 금지합니다. + +서비스 이용과 관련하여 분쟁이 발생할 경우, 회원은 회사의 해결 절차를 성실히 따라야 합니다. + + + +제 5 조 (서비스 중단 및 변경) + +회사는 서비스 운영상 필요한 경우 사전 공지를 통해 서비스의 일부 또는 전부를 변경하거나 중단할 수 있습니다. + +불가항력적인 사유(서버 장애, 법적 규제 등)로 인해 서비스가 중단될 경우, 회사는 이에 대한 책임을 지지 않습니다. + + + +제 6 조 (면책조항) + +회사는 멘토링 과정에서 발생하는 문제에 대해 법적 책임을 지지 않습니다. + +회사는 회원이 제공하는 정보의 신뢰성, 정확성에 대해 보증하지 않습니다. + + + +제 7 조 (준거법 및 분쟁 해결) + +본 약관과 관련된 분쟁은 대한민국 법률을 따르며, 회사와 회원 간 분쟁 발생 시 상호 협의하여 해결하도록 합니다. \ No newline at end of file diff --git a/src/global/components/JobCategories.tsx b/src/global/components/JobCategories.tsx index 5ea0b43..f2c82c7 100644 --- a/src/global/components/JobCategories.tsx +++ b/src/global/components/JobCategories.tsx @@ -14,7 +14,7 @@ export const fieldOptions: string[] = [ ]; // 직무 (세부 카테고리) -export const subFieldOptions: { [key: string]: string[] } = { +export const subFieldOptions: Record = { 전체: [], 마케팅: [ '브랜드 마케팅', @@ -120,85 +120,3 @@ export const subFieldOptions: { [key: string]: string[] } = { '기타', ], }; - -export const jobCategoryMapping: { [key: string]: string } = { - 마케팅: 'MARKETING', - '서비스 기획': 'SERVICE_PLANNING', - 디자인: 'DESIGN', - 개발: 'DEVELOPMENT', - 대학원: 'GRADUATE_SCHOOL', - 인사: 'HR_SUPPORT', - 영업: 'SALES_CUSTOMER', - 금융: 'FINANCE_CONSULTING_VC', - 데이터: 'DATA', - 의료: 'MEDICAL', - 법률: 'LEGAL', -}; - -export const detailedJobMapping: { [key: string]: string } = { - '브랜드 마케팅': 'BRAND_MARKETING', - '퍼포먼스 마케팅': 'PERFORMANCE_MARKETING', - '디지털/소셜 마케팅': 'DIGITAL_SOCIAL_MARKETING', - '그로스 마케팅': 'GROWTH_MARKETING', - PR: 'PR', - AE: 'AE', - '콘텐츠 마케팅': 'CONTENT_MARKETING', - '크리에이티브 디렉팅': 'CREATIVE_DIRECTING', - 카피라이터: 'COPYWRITER', - '미디어 플래너': 'MEDIA_PLANNER', - '방송PD/영상PD': 'BROADCAST_PD', - 서비스기획: 'SERVICE_PLANNING', - 'PM/PO': 'PM_PO', - '전략 기획': 'STRATEGY_PLANNING', - '운영/기획': 'OPERATION_PLANNING', - '사업 개발': 'BUSINESS_DEVELOPMENT', - 'CX 매니저': 'CX_MANAGER', - 창업: 'STARTUP', - 'UX/UI디자인': 'UX_UI_DESIGN', - '그래픽 디자인': 'GRAPHIC_DESIGN', - '상품 디자인': 'PRODUCT_DESIGN', - '브랜드 디자인': 'BRAND_DESIGN', - '웹 디자인': 'WEB_DESIGN', - '아트 디렉터': 'ART_DIRECTOR', - 프론트엔드: 'FRONTEND', - 백엔드: 'BACKEND', - '풀스택 개발자': 'FULLSTACK', - 'iOS/Android 개발자': 'IOS_ANDROID', - 'DevOps 엔지니어': 'DEVOPS', - '클라우드 엔지니어': 'CLOUD', - '시스템/네트워크 엔지니어': 'SYSTEM_NETWORK', - '보안 엔지니어': 'SECURITY', - '국내 대학원': 'DOMESTIC_GRADUATE_SCHOOL', - '해외 대학원': 'OVERSEAS_GRADUATE_SCHOOL', - 인사기획: 'HR_PLANNING', - 채용담당: 'RECRUITMENT', - '인재육성/교육담당': 'TALENT_DEVELOPMENT', - 조직문화담당: 'ORGANIZATION_CULTURE', - 노무담당: 'LABOR', - '총무/경영지원': 'GENERAL_AFFAIRS', - 리크루터: 'RECRUITER', - '기업영업(B2B)': 'B2B_SALES', - '개인영업(B2C)': 'B2C_SALES', - 해외영업: 'OVERSEAS_SALES', - 기술영업: 'TECHNICAL_SALES', - 컨설턴트: 'CONSULTANT', - 'VC/투자': 'VC_INVESTMENT', - 'IB/PE/대체투자': 'IB_PE_ALTERNATIVE_INVESTMENT', - 에널리스트: 'ANALYST', - '데이터 사이언티스트': 'DATA_SCIENTIST', - '데이터 엔지니어': 'DATA_ENGINEER', - '데이터 애널리스트': 'DATA_ANALYST', - 'BI 엔지니어': 'BI_ENGINEER', - '머신러닝 엔지니어': 'MACHINE_LEARNING_ENGINEER', - 임상의사: 'CLINICAL_DOCTOR', - 임상연구원: 'CLINICAL_RESEARCHER', - '의료기기 연구개발': 'MEDICAL_DEVICE_RND', - '제약회사 연구원': 'PHARMACEUTICAL_RESEARCHER', - '바이오 연구원': 'BIO_RESEARCHER', - 변호사: 'LAWYER', - 법무담당: 'LEGAL_COUNSEL', - 특허담당: 'PATENT', - '준법감시인(컴플라이언스)': 'COMPLIANCE', - '법무법인 사무직': 'LAW_FIRM_STAFF', - 법률자문: 'LEGAL_ADVISOR', -}; diff --git a/src/mentorship/pages/MentorInfoView.tsx b/src/mentorship/pages/MentorInfoView.tsx index 74f3f5c..d7ecdf2 100644 --- a/src/mentorship/pages/MentorInfoView.tsx +++ b/src/mentorship/pages/MentorInfoView.tsx @@ -1,13 +1,9 @@ import { useState, useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { ArrowLeft } from 'lucide-react'; -import { Bookmark } from 'lucide-react'; +import { ArrowLeft, Bookmark } from 'lucide-react'; import { GlobalButton } from '@/global/ui/GlobalButton'; - import { getMentorById } from '../api/MentorViewApi'; import { MentorData } from '@/user/types'; -import { reverseJobCategoryMapping, reverseDetailedJobMapping } from '@/user/components/Mapping'; -import { reverseMentoringFieldMapping, reverseDayMapping } from '@/user/components/Mapping'; import MentorInfoProfile from '../../user/components/profileInfo/MentorInfoProfile'; import MentorInfoBio from '../../user/components/profileInfo/MentorInfoBio'; @@ -27,116 +23,76 @@ const MentorInfoView = () => { try { if (!mentorId || isNaN(Number(mentorId))) return; const data = await getMentorById(Number(mentorId)); - - // 매핑 - const localizedData: MentorData = { - ...data, - mentoringField: data.mentoringField.map((f) => reverseMentoringFieldMapping[f] || f), - timezone: { - ...data.timezone, - days: data.timezone.days.map((d) => reverseDayMapping[d] || d), - }, - organization: { - ...data.organization, - jobCategory: reverseJobCategoryMapping[data.organization.jobCategory] || data.organization.jobCategory, - detailedJob: reverseDetailedJobMapping[data.organization.detailedJob] || data.organization.detailedJob, - }, - }; - - setMentorData(localizedData); + // 백엔드에서 이미 한국어 필드가 내려오므로 그대로 사용 + setMentorData(data); } catch (err) { - console.error("멘토 상세 불러오기 실패:", err); + console.error('멘토 상세 불러오기 실패:', err); } }; - fetchData(); }, [mentorId]); useEffect(() => { - const storedBookmark = localStorage.getItem("isBookmarked"); - setIsBookmarked(storedBookmark === "true"); + const stored = localStorage.getItem('isBookmarked'); + setIsBookmarked(stored === 'true'); }, []); const toggleBookmark = () => { - const newBookmarkState = !isBookmarked; - setIsBookmarked(newBookmarkState); - localStorage.setItem("isBookmarked", newBookmarkState.toString()); + const next = !isBookmarked; + setIsBookmarked(next); + localStorage.setItem('isBookmarked', next.toString()); }; if (!mentorData) return
Loading...
; return (
-
- - {/* 보라색 */}
- - {/* 헤더 */}
navigate("/mentorship")} + onClick={() => navigate('/mentorship')} />
- - {/* 프로필 섹션 */} - -
- -
- - {/* 자기소개 */} - - - {/* 선호 시간대 */} - - - {/* 멘토링 분야 & 해시태그 */} - - - {/* 멘티에게 전하고 싶은 말 */} - - - {/* 포트폴리오 */} -
+ + + + +
- - {/* 멘토링 제안하기 버튼 */}
- navigate("/mentorship/suggestion")} - > + navigate('/mentorship/suggestion')}> 멘토링 제안하기
@@ -144,4 +100,4 @@ const MentorInfoView = () => { ); }; -export default MentorInfoView; \ No newline at end of file +export default MentorInfoView; diff --git a/src/user/components/Mapping.ts b/src/user/components/Mapping.ts index acc3f9b..de87ce5 100644 --- a/src/user/components/Mapping.ts +++ b/src/user/components/Mapping.ts @@ -1,51 +1,12 @@ -import { jobCategoryMapping, detailedJobMapping } from "@/global/components/JobCategories"; - -// 직무 (한글로) -export const reverseJobCategoryMapping: { [key: string]: string } = Object.entries(jobCategoryMapping) - .reduce((acc, [kor, eng]) => { - acc[eng] = kor; - return acc; - }, {} as { [key: string]: string }); - - -// 세부 직무 (한글로) -export const reverseDetailedJobMapping: { [key: string]: string } = Object.entries(detailedJobMapping) - .reduce((acc, [kor, eng]) => { - acc[eng] = kor; - return acc; - }, {} as { [key: string]: string }); - - - -// 멘토링 분야 (영어로) -export const mentoringFieldMapping: Record = { - '취업 준비': 'JOB_PREPARATION', - '면접 대비': 'INTERVIEW_PREPARATION', - '업계 동향': 'PRACTICAL_SKILLS', - '커리어 고민': 'PORTFOLIO', -}; - -// 멘토링 분야 (한글로) -export const reverseMentoringFieldMapping: Record = Object.entries(mentoringFieldMapping).reduce((acc, [kor, eng]) => { - acc[eng] = kor; - return acc; -}, {} as Record); - - - -// 요일 (영어로) -export const dayMapping: Record = { - '월': 'MON', - '화': 'TUE', - '수': 'WED', - '목': 'THU', - '금': 'FRI', - '토': 'SAT', - '일': 'SUN', -}; - -// 요일 (한글로) -export const reverseDayMapping: Record = Object.entries(dayMapping).reduce((acc, [kor, eng]) => { - acc[eng] = kor; - return acc; -}, {} as Record); \ No newline at end of file +// 매핑 로직 제거 +// 백엔드와 프론트엔드가 동일한 한국어 문자열만 주고받으므로 +// 별도의 English ↔ 한국어 매핑 로직은 더 이상 필요하지 않습니다. + +// 필요한 경우 아래와 같이 한국어 필드 옵션만 export하세요. +export { + fieldOptions, + subFieldOptions, +} from '@/global/components/JobCategories'; + +// MentorFields 역시 한국어 문자열을 그대로 사용합니다. +// 추가 매핑 파일은 사용되지 않습니다. diff --git a/src/user/components/profile/mentor/MentorFields.tsx b/src/user/components/profile/mentor/MentorFields.tsx index e8ff5ba..2ce829e 100644 --- a/src/user/components/profile/mentor/MentorFields.tsx +++ b/src/user/components/profile/mentor/MentorFields.tsx @@ -5,20 +5,11 @@ interface MentorFieldsProps { onChange: (updatedData: MentorProfileData) => void; } -const mentoringFieldMap: Record = { - '취업 준비': 'JOB_PREPARATION', - '업계 동향': 'PRACTICAL_SKILLS', - '면접 대비': 'INTERVIEW_PREPARATION', - '커리어 고민': 'PORTFOLIO', -}; - const MentorFields = ({ mentorData, onChange }: MentorFieldsProps) => { - const toggleSelection = (field: string) => { - const fieldKey = mentoringFieldMap[field]; - - const updatedFields = mentorData.mentoringField.includes(fieldKey) - ? mentorData.mentoringField.filter((f) => f !== fieldKey) - : [...mentorData.mentoringField, fieldKey]; + const toggleSelection = (fieldTitle: string) => { + const updatedFields = mentorData.mentoringField.includes(fieldTitle) + ? mentorData.mentoringField.filter((f) => f !== fieldTitle) + : [...mentorData.mentoringField, fieldTitle]; onChange({ ...mentorData, @@ -38,23 +29,20 @@ const MentorFields = ({ mentorData, onChange }: MentorFieldsProps) => {
멘토링 분야를 선택해주세요!
{fields.map((field) => { - const isSelected = mentorData.mentoringField.includes( - mentoringFieldMap[field.title], - ); + const isSelected = mentorData.mentoringField.includes(field.title); return ( diff --git a/src/user/components/profile/mentor/MentorProfile1.tsx b/src/user/components/profile/mentor/MentorProfile1.tsx index 0065a90..c1e4c62 100644 --- a/src/user/components/profile/mentor/MentorProfile1.tsx +++ b/src/user/components/profile/mentor/MentorProfile1.tsx @@ -6,8 +6,6 @@ import { uploadProfileImage } from '@/user/api/profileApi'; import { fieldOptions, subFieldOptions, - jobCategoryMapping, - detailedJobMapping, } from '@/global/components/JobCategories'; import ErrorModal from '@/global/ui/ErrorModal'; @@ -26,12 +24,12 @@ const MentorProfile1 = ({ setMentorData, onNext, }: MentorProfile1Props) => { - const [isUploading, setIsUploading] = useState(false); // 이미지 업로드 상태 + const [isUploading, setIsUploading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const [nicknameError, setNicknameError] = useState( undefined, - ); // 닉네임 에러 메시지 상태 수정 - const [isNicknameValid, setIsNicknameValid] = useState(false); // 닉네임 유효성 상태 추가 + ); + const [isNicknameValid, setIsNicknameValid] = useState(false); const [successMessage, setSuccessMessage] = useState( undefined, ); @@ -42,12 +40,10 @@ const MentorProfile1 = ({ } }, [mentorData.nickname, nicknameError]); - // 프로필 이미지 선택 후 API 요청하여 업로드 const handleImageUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; - // 10MB 초과 확인 (10MB = 10 * 1024 * 1024 바이트) if (file.size > 10 * 1024 * 1024) { setErrorMessage( '파일 크기가 너무 큽니다.
10MB 이하의 이미지를 업로드해주세요.', @@ -57,65 +53,50 @@ const MentorProfile1 = ({ setIsUploading(true); try { - const imageUrl = await uploadProfileImage(file, 'ROLE_MENTOR'); // memberType 추가 + const imageUrl = await uploadProfileImage(file, 'ROLE_MENTOR'); setMentorData({ ...mentorData, image: { fileName: file.name, filePath: imageUrl }, }); - } catch (error) { + } catch { setErrorMessage('이미지 업로드에 실패했습니다.'); } finally { setIsUploading(false); } }; - // 닉네임 유효성 체크 const handleNicknameCheck = async () => { - const trimmedNickname = mentorData.nickname.trim(); // 공백 제거 - - console.log('검사 닉네임: ', trimmedNickname); - - if (!trimmedNickname) { + const trimmed = mentorData.nickname.trim(); + if (!trimmed) { setNicknameError('닉네임을 입력해주세요.'); setIsNicknameValid(false); - setSuccessMessage(undefined); // 닉네임 검사를 새로 시작하면 이전 성공 메시지는 초기화 + setSuccessMessage(undefined); return; } try { - const response = await axiosInstance.get( - `https://api.ringus.my/v1/members/check-nickname`, - { params: { nickname: mentorData.nickname.trim() } }, - ); - //console.log('응답 데이터:', response.data); - - if (response.data.data === false) { - // 닉네임이 이미 존재하는 경우 - setNicknameError( - response.data.message || '이미 사용 중인 닉네임입니다.', - ); + const { data } = await axiosInstance.get('/v1/members/check-nickname', { + params: { nickname: trimmed }, + }); + if (data.data === false) { + setNicknameError(data.message || '이미 사용 중인 닉네임입니다.'); setIsNicknameValid(false); - setSuccessMessage(undefined); // 성공 메시지 초기화 - } else { - // 닉네임이 사용 가능할 때 - setNicknameError(undefined); // 에러 초기화 - setSuccessMessage('닉네임이 유효합니다.'); // 성공 메시지 설정 - setIsNicknameValid(true); // 유효성 체크 성공 - } - } catch (error: any) { - if (error.response) { - setNicknameError( - error.response.data.message || '알 수 없는 오류가 발생했습니다.', - ); + setSuccessMessage(undefined); } else { - setNicknameError('네트워크 오류가 발생했습니다. 다시 시도해주세요.'); + setNicknameError(undefined); + setSuccessMessage('닉네임이 유효합니다.'); + setIsNicknameValid(true); } + } catch (err: any) { + setNicknameError( + err.response?.data?.message || + '네트워크 오류가 발생했습니다. 다시 시도해주세요.', + ); setIsNicknameValid(false); - setSuccessMessage(undefined); // 실패 시 성공 메시지 초기화 + setSuccessMessage(undefined); } }; - // 전체 입력 필드 유효성 체크 const isFormValid = mentorData.nickname.trim().length > 0 && mentorData.education.schoolName.trim().length > 0 && @@ -128,7 +109,6 @@ const MentorProfile1 = ({ return (
- {/* 상단 제목과 설명 */} {errorMessage && (
- {/* 프로필 이미지 업로드 */} + + {/* 이미지 업로드 */}
-
+
{mentorData.image?.filePath ? ( 프로필 이미지 ) : (
@@ -175,13 +156,13 @@ const MentorProfile1 = ({ onChange={handleImageUpload} />
+ {/* 닉네임 입력 */}
setMentorData({ ...mentorData, nickname: e.target.value }) } @@ -191,13 +172,13 @@ const MentorProfile1 = ({ successMessage={successMessage} />
- {/* 학교 입력 */} + + {/* 학력, 전공, 조직 */}
setMentorData({ ...mentorData, @@ -209,31 +190,24 @@ const MentorProfile1 = ({ } />
- {/* 전공 입력 */}
setMentorData({ ...mentorData, - education: { - ...mentorData.education, - major: e.target.value, - }, + education: { ...mentorData.education, major: e.target.value }, }) } />
- {/* 회사 입력 */}
setMentorData({ ...mentorData, @@ -246,6 +220,7 @@ const MentorProfile1 = ({ />
+ {/* 직무 선택 */}
{ + onChange={({ jobCategory, detailedJob }) => { setMentorData({ ...mentorData, organization: { ...mentorData.organization, - jobCategory: - jobCategoryMapping[value.jobCategory] || value.jobCategory, // ✅ 한글 → 영어 변환 - detailedJob: - detailedJobMapping[value.detailedJob] || value.detailedJob, // ✅ 한글 → 영어 변환 + jobCategory, + detailedJob, }, }); }} />
+ + {/* 경력 선택 */}
- {/* 경력 선택 모달 */} { + setExperience={(value) => setMentorData({ ...mentorData, - organization: { - ...mentorData.organization, - experience: value, // 숫자로 저장 - }, - }); - }} + organization: { ...mentorData.organization, experience: value }, + }) + } />
- {/* 버튼 */} + + {/* 다음 버튼 */}
Date: Wed, 14 May 2025 22:27:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20Enum=20=ED=95=9C=EA=B8=80=20?= =?UTF-8?q?=EA=B0=92=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../profile/mentor/MentorProfile1.tsx | 4 +- .../components/profile/mentor/MentorTime.tsx | 51 ++++++++----------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/user/components/profile/mentor/MentorProfile1.tsx b/src/user/components/profile/mentor/MentorProfile1.tsx index c1e4c62..d338c45 100644 --- a/src/user/components/profile/mentor/MentorProfile1.tsx +++ b/src/user/components/profile/mentor/MentorProfile1.tsx @@ -104,7 +104,7 @@ const MentorProfile1 = ({ mentorData.organization.name.trim().length > 0 && mentorData.organization.jobCategory.trim().length > 0 && mentorData.organization.detailedJob.trim().length > 0 && - mentorData.organization.experience !== null && + mentorData.organization.experience > 0 && isNicknameValid; return ( @@ -129,7 +129,7 @@ const MentorProfile1 = ({ {/* 이미지 업로드 */}
-
+
{mentorData.image?.filePath ? ( { const [isModalOpen, setIsModalOpen] = useState(false); - const daysMap: Record = { - 월: 'MON', - 화: 'TUE', - 수: 'WED', - 목: 'THU', - 금: 'FRI', - 토: 'SAT', - 일: 'SUN', - }; - // 시작 및 종료 시간 초기값 설정 const [startHour, setStartHour] = useState( parseInt(mentorData.timezone.startTime.split(':')[0], 10) || 8, @@ -35,15 +25,15 @@ const MentorTime = ({ mentorData, setMentorData }: MentorTimeProps) => { parseInt(mentorData.timezone.endTime.split(':')[1], 10) || 0, ); - // 요일 선택 + // 요일 선택: mentorData.timezone.days는 한국어 요일 문자열 배열 const toggleDaySelection = (day: string) => { setMentorData((prev) => ({ ...prev, timezone: { ...prev.timezone, - days: prev.timezone.days.includes(daysMap[day]) - ? prev.timezone.days.filter((d) => d !== daysMap[day]) // 선택 해제 - : [...prev.timezone.days, daysMap[day]], // 선택 추가 + days: prev.timezone.days.includes(day) + ? prev.timezone.days.filter((d) => d !== day) + : [...prev.timezone.days, day], }, })); }; @@ -78,20 +68,23 @@ const MentorTime = ({ mentorData, setMentorData }: MentorTimeProps) => { {/* 요일 선택 UI */}
요일
- {Object.keys(daysMap).map((day) => ( - - ))} + {['월', '화', '수', '목', '금', '토', '일'].map((day) => { + const isSelected = mentorData.timezone.days.includes(day); + return ( + + ); + })}
{/* 시간대 */} @@ -115,7 +108,7 @@ const MentorTime = ({ mentorData, setMentorData }: MentorTimeProps) => { setEndHour={setEndHour} endMinute={endMinute} setEndMinute={setEndMinute} - onSave={handleTimeSave} // mentorData에 저장하는 함수 + onSave={handleTimeSave} /> {/* 선택 완료 버튼 */} From 419fe09585db3405f754f4d27c1cde1c26c16fb3 Mon Sep 17 00:00:00 2001 From: Junsub Lee Date: Mon, 19 May 2025 03:00:52 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20jobcategories=20'=EC=A0=84?= =?UTF-8?q?=EC=B2=B4'=20=EB=B6=84=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global/components/JobCategories.tsx | 14 ++++++++++++++ src/home/pages/HomePage.tsx | 4 ++-- .../components/profile/mentor/MentorProfile1.tsx | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/global/components/JobCategories.tsx b/src/global/components/JobCategories.tsx index ebe240d..681b75f 100644 --- a/src/global/components/JobCategories.tsx +++ b/src/global/components/JobCategories.tsx @@ -14,6 +14,20 @@ export const fieldOptions: string[] = [ '법률', ]; +export const registerfieldOptions: string[] = [ + '마케팅', + '서비스 기획', + '디자인', + '개발', + '대학원', + '인사', + '영업', + '금융', + '데이터', + '의료', + '법률', +]; + // 직군별 아이콘 매핑 export const fieldIcons: { [key: string]: string } = { 전체: '/assets/main/overall.png', diff --git a/src/home/pages/HomePage.tsx b/src/home/pages/HomePage.tsx index 2186721..3fcfee6 100644 --- a/src/home/pages/HomePage.tsx +++ b/src/home/pages/HomePage.tsx @@ -114,7 +114,7 @@ export default function HomePage() { {/* 카테고리 아이콘 리스트 */}
-
+
{/* 추천 멘토 섹션 */} -
+

회원님이 관심있어 할 멘토 추천

diff --git a/src/user/components/profile/mentor/MentorProfile1.tsx b/src/user/components/profile/mentor/MentorProfile1.tsx index d338c45..c64c13c 100644 --- a/src/user/components/profile/mentor/MentorProfile1.tsx +++ b/src/user/components/profile/mentor/MentorProfile1.tsx @@ -4,7 +4,7 @@ import { AuthInputBox } from '@/auth/components/AuthInputBox'; import { MentorProfileData } from '@/user/types/profileTypes'; import { uploadProfileImage } from '@/user/api/profileApi'; import { - fieldOptions, + registerfieldOptions, subFieldOptions, } from '@/global/components/JobCategories'; @@ -225,7 +225,7 @@ const MentorProfile1 = ({ Date: Mon, 19 May 2025 21:14:55 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20merge=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mentorship/pages/MentorInfoView.tsx | 62 ++++++++++++++++--------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/mentorship/pages/MentorInfoView.tsx b/src/mentorship/pages/MentorInfoView.tsx index a80fab1..6de4b60 100644 --- a/src/mentorship/pages/MentorInfoView.tsx +++ b/src/mentorship/pages/MentorInfoView.tsx @@ -27,24 +27,24 @@ const MentorInfoView = () => { try { if (!mentorId || isNaN(Number(mentorId))) return; const data = await getMentorById(Number(mentorId)); - // 백엔드에서 이미 한국어 필드가 내려오므로 그대로 사용 setMentorData(data); } catch (err) { console.error('멘토 상세 불러오기 실패:', err); } }; + fetchData(); }, [mentorId]); useEffect(() => { - const stored = localStorage.getItem('isBookmarked'); - setIsBookmarked(stored === 'true'); + const storedBookmark = localStorage.getItem('isBookmarked'); + setIsBookmarked(storedBookmark === 'true'); }, []); const toggleBookmark = () => { - const next = !isBookmarked; - setIsBookmarked(next); - localStorage.setItem('isBookmarked', next.toString()); + const newBookmarkState = !isBookmarked; + setIsBookmarked(newBookmarkState); + localStorage.setItem('isBookmarked', newBookmarkState.toString()); }; if (!mentorData) return
Loading...
; @@ -52,7 +52,9 @@ const MentorInfoView = () => { return (
+ {/* 보라색 */}
+ {/* 헤더 */}
{ onClick={toggleBookmark} />
+ + {/* 프로필 섹션 */} { detailedJob={mentorData.organization.detailedJob} experience={mentorData.organization.experience} mentoringCount={mentorData.mentoringCount} - image={mentorData.image?.filePath || '/assets/ringusprofile.png'} + image={mentorData?.image?.filePath || '/assets/ringusprofile.png'} + /> +
+ +
+ {/* 자기소개 */} + + + {/* 선호 시간대 */} + + + {/* 멘토링 분야 & 해시태그 */} + + + {/* 멘티에게 전하고 싶은 말 */} + + + {/* 포트폴리오 */} +
- - - - -
+ + {/* 멘토링 제안하기 버튼 */}
navigate(`/mentorship/suggestion/${mentorId}`)}