$ npm install
$ npm run dev- 프로젝트 한줄요약: 알바생과 사장님을 연결해주는 일자리 매칭 플랫폼
- 서비스 소개
- 사장님은 가게 정보와 구인 공고를 등록
- 알바생은 원하는 조건에 맞는 공고를 검색해 쉽게 지원
- 공고에 제시된 시급이 기본시급보다 얼마나 높은지 표시되어, 알바생은 더 나은 조건의 일자리 합리적으로 선택 가능
- 매칭 결과는 알림으로 빠르게 받아볼 수 있어, 구직과 채용 과정 모두 간편하게 진행
project/
├── .github/ # 이슈·PR 템플릿·워크플로
│ ├── ISSUE_TEMPLATE/
│ ├── scripts/
│ └── workflows/
├── .husky/ # Git 훅 스크립트
├── public/ # 정적 리소스 루트
│ ├── _redirects # Netlify 리다이렉트 규칙
│ ├── default-image.png # 가게 기본 이미지
│ └── tjg_favicon.png # 파비콘
├── scripts/ # 배포·빌드 자동화 스크립트
├── src/ # 애플리케이션 소스
│ ├── apis/ # API 래퍼
│ ├── assets/ # 이미지·폰트 등
│ ├── components/ # 재사용 UI 컴포넌트
│ ├── constants/ # 상수 정의
│ ├── hooks/ # 커스텀 훅
│ ├── layouts/ # 공통 레이아웃
│ ├── pages/ # 페이지 컴포넌트
│ ├── store/ # Zustand 등 전역 상태
│ ├── styles/ # 전역·공통 스타일
│ ├── types/ # TypeScript 타입
│ ├── utils/ # 유틸리티 함수
│ ├── App.tsx # 루트 컴포넌트
│ ├── main.tsx # 엔트리 포인트
│ └── Router.tsx # 라우터 설정
├── package.json # 의존성·스크립트 정의
├── .gitignore # Git 무시 목록
├── .prettierrc # Prettier 코드 스타일
├── eslint.config.js # ESLint 설정
├── index.html # SPA 루트 HTML
├── netlify.toml # Netlify 배포 설정
├── tailwind.config.js # TailwindCSS 설정
├── tsconfig.json # TypeScript 기본 설정
├── vite.config.ts # Vite 번들러 설정
└── README.md # 프로젝트 개요
| 오수빈 | 지정환 | 진성진 | 홍수민 |
|---|---|---|---|
| TL, FE | FE | FE | FE |
| GitHub | GitHub | GitHub | GitHub |
| 이름 | 작업 |
|---|---|
| 오수빈 |
|
| 지정환 |
|
| 진성진 |
|
| 홍수민 |
|
Origin 레포지토리 링크(모든 페이지를 확인하시려면 클릭해주세요 😆)
- 내 프로필이 등록되어 있지 않다면 등록하기 버튼을 통해 프로필 등록 페이지로 이동할 수 있습니다.
- 내 프로필 정보와 지원 리스트를 확인할 수 있습니다.
- 프로필 편집 페이지로 버튼을 통해 이동할 수 있습니다.
- 지원 리스트는 페이지네이션으로 넘길 수 있습니다.
- 등록한 공고의 정보와 지원자를 확인할 수 있습니다.
- 지원자의 거절/승인 상태를 결정할 수 있습니다.
- 공고 편집 페이지로 버튼을 통해 이동할 수 있으나, 내 가게가 아니거나 지난 공고이거나 지원자가 있다면 버튼이 비활성화됩니다.
- 지원하기 버튼을 통해 공고에 지원할 수 있습니다.
- 지원을 한 공고에 대해서는 취소하기 버튼을 통해 지원 취소가 가능합니다.
- 최근에 본 공고가 최대 6개 띄워집니다.
TextField (Origin Repo Wiki)
Table (Origin Repo Wiki)
Pagination (Origin Repo Wiki)
- 권한 별 페이지 접근 제어를 위해 구현하게 되었음
- 기존 문제점: 기존 다른 팀원이 각각의 페이지마다 접근 권한을 위한 로직을 추가
- 해결하고자 한 문제: Router 파일 하나에서 제어할 수 있도록 일원화
-
현재 뷰포트 기준으로 협의된 디바이스를 반환
-
리사이즈 이벤트를 감지, 브라우저 창의 크기를 조절해도 그에 맞는 디바이스를 문자열로 반환
-
useRemoveTopPageScroll커스텀 훅에서 활용function useRemoveTopPageScroll(props: PropsType) { const device = useBreakpoint(); // 활용 useEffect(() => { const deviceCondition = observeDevices.includes(device); // ... }, [...]); }
- 특정 디바이스 뷰포트 일 때, 가장 최상단의 페이지 스크롤을 제거
- 모바일 뷰포트에서 화면 전체를 덮는 알림창이 있는 경우, 최상단의 페이지 스크롤을 제거할 필요가 있었음
- 사용 예시
useRemoveTopPageScroll({ observeDevices: ["mobile"], // 모바일 디바이스 크기에서 condition: showFilter, // showFilter가 true인 경우에 최상단 스크롤을 제거 });
-
IntersectionObserver API를 커스텀 훅으로 재사용 가능토록 구현
-
특정 ref를 전달한 요소가 뷰포트 내로 진입되었을 때, 콜백 함수 실행
-
사용 예시
const targetRef = useIntersection<HTMLDivElement>({ callback: ([entry]) => { if (entry.isIntersecting && !isLoading && hasNext) { refetch(); } }, }); return ( ... <div ref={targetRef} /> ... )
- as props 기반 통합 컴포넌트에서 복잡한 타입 정의와 타입 단언으로 인해 가독성과 타입 안정성이 저하됨
- TextField.Input / TextField.TextArea로 분리하고 공통 UI는 Field 컴포넌트로 추출하여 역할 명확화
- 타입 추론이 명확해지고 컴포넌트 재사용성과 유지보수성이 크게 향상됨
- 반복되는 권한 조건 로직을 ProtectedRoute 컴포넌트로 추상화하여, 조건 선언을 라우터 파일에서 일관되게 관리
- 조건별 모달 및 리다이렉트 처리까지 포함해 페이지 내부 로직을 단순화하고 재사용성 향상
- 향후 라우터 파일 확장을 고려해 도메인 단위 분리 전략도 사전에 구상하여 구조적 확장성에 대비
- 모바일 환경에서 모달 사용 시 스크롤이 남는 문제를 해결하기 위해 디바이스 타입과 조건 기반의 useRemoveTopPageScroll 훅을 구현
- resize 이벤트를 debounce 처리한 useBreakpoint 훅과 조합해 다양한 디바이스 상황에서도 스크롤 상태를 안정적으로 제어
- 스크롤 제어 로직을 재사용 가능한 훅으로 분리하여 페이지/모달/바텀시트 등 다양한 컴포넌트에서 일관된 UX 유지 가능
- 반복되는 IntersectionObserver 로직을 추상화하여 useIntersection 커스텀 훅으로 분리
- callback과 deps만 넘기면 자동 관찰 및 해제가 가능해져 재사용성과 코드 일관성 확보
- 타입 제네릭 및 의존성 처리까지 고려해 다양한 요소에 범용적으로 대응 가능하게 설계






