본문바로가기
Allwage
구직자·사업주 양면 알바 매칭 · RN WebView 앱
Allwage
구직자·사업주 양면 알바 매칭 · RN WebView 앱
Allwage
Allwage
구직자·사업주 양면 알바 매칭 모바일 플랫폼
  • Next.js 13
  • React 18
  • TypeScript
  • Tailwind CSS
  • Recoil
  • NextAuth
  • React Hook Form
  • Zod
  • Expo (RN WebView)
  • NestJS
  • MySQL
  • TypeORM
  • AWS
  • Project
    Allwage (올웨이지)
  • Period
    2023.11–12 · 2024.08–10 (약 5개월, 2단계)
  • Type
    Mobile Web App (RN WebView) / 양면 매칭
  • Role
    구직자 도메인 풀스택 · 2인 협업
서비스
구직자·사업주 양면 알바 구인·구직 매칭 (app.allwage.co.kr)
담당
구직자 도메인 풀스택(프론트+API) · 사업주 도메인은 팀원 (2인 협업)
Frontend
Next.js 13 (App Router), React 18, TypeScript, Tailwind CSS, Recoil, NextAuth, React Hook Form + Zod, Axios
Backend
NestJS, MySQL, TypeORM, Swagger — 구직자 도메인 API 직접 구현
App
Expo(React Native) WebView 셸 — 카메라·위치 등 네이티브 기능 브릿지
핵심 설계
session.isCompany 역할 분기 · api/service/dto/types 4계층 · 공통 코드 캐싱 · Zod refine + Sentinel 폼 검증
구직자 대시보드
구직자 — 대시보드
구직자 이력서 관리
구직자 — 이력서 관리
구인공고 검색·필터·지도
Job
Search

리스트와 카카오맵 뷰를 오가며 공고를 탐색합니다. 등록·조회수·지원자수 기준 6종 정렬, 지역·업종·급여·요일·마감여부 다중 필터를 제공하고, 찜(Like)은 lodash debounce(300ms) + 낙관적 UI로 연타를 방지했습니다. ‘내 주변 공고’는 앱 WebView가 위치 권한을 네이티브로 처리해 위경도 기반으로 조회합니다.

이력서 상세
Resume
Management

최대 3개 규칙의 이력서 CRUD. 희망 근무조건·경력·자격증·자기소개를 React Hook Form + Zod로 검증하는데, “요일을 고르거나 ‘협의 가능’을 켜야 한다” 같은 교차필드 규칙을 Zod refine()으로 구현했습니다. 경력·자격증처럼 개수가 유동적인 카드는 Sentinel(placeholder) 패턴으로 항상 1개를 유지하되 제출 시 DTO가 걸러냅니다.

구직자 마이페이지
My Page
(Job Seeker)

구직자 마이페이지는 프로필 카드(이름·생년·성별·연락처)와, 대시보드 메인화면의 활동축과 이어지는 기능 메뉴를 우선 노출합니다 — 이력서 관리 → 찜한 공고 → 알바 줄서기 → 지원한 공고 순으로 배치하고, 이어서 비밀번호 재설정·공지·FAQ 등 계정 메뉴를 둡니다.

같은 /my-page URL을 isJobSeekerUser()로 분기해 구직자 전용 컴포넌트(JobSeekerMyPage)를 렌더하고, 내부 라우팅과 외부 링크(카카오톡·FAQ)를 NavigationItem으로 일원화했습니다.

알바 줄서기
Standby
Queue

마감된 공고에도 ‘알바 줄서기’로 대기열에 지원할 수 있습니다. 내가 줄 선 공고는 마이페이지에서 관리하며(linealba API), 대시보드의 활동 현황 카드와 연동됩니다. 종료된 공고에서만 노출되도록 상태값으로 분기해, 구직자가 놓친 기회도 이어서 잡도록 설계했습니다.

Challenge 1 | 역할로 분리된 2인 개발에서 구직자 여정을 일관 구현

상황: 구직자·사업주 두 사용자가 하나의 코드베이스를 쓰되 완전히 다른 권한·화면·데이터를 봐야 했습니다. 2인이 역할별로 나눠(구직자·사업주) 개발했기에, 두 도메인이 공유하는 역할 분기 지점을 흐트러짐 없이 잡는 것이 관건이었습니다.

판단: 팀과 함께 역할 판별의 뼈대를 세션 플래그(isCompany) → 유틸(getUserType/isJobSeekerUser) → 진입점·컴포넌트 트리 교체로 계층화해 합의하고, 그 위에서 각자 도메인을 구현하면 분기를 한곳에서 통제할 수 있다고 판단했습니다.

액션: 합의한 역할 모델 위에서 구직자 진입 리다이렉트, 공용 화면(/my-page 등)의 구직자 컴포넌트 스왑, 찜·카드 등 구직자 세부 조건 렌더링을 담당하고, 구직자 도메인 API의 접근 검증을 서버에서 처리했습니다.

결과: 역할별로 나눠 개발해도 구직자 여정이 조건문 산재 없이 일관되게 유지되고, 합의한 분기 규칙 덕에 두 도메인이 충돌 없이 통합됐습니다.

Challenge 2 | 4계층 + DTO로 API 스키마와 화면 모델 분리

상황: 백엔드 응답 필드(type·days·times·period)를 화면에서 그대로 쓰면 스키마 변경이 컴포넌트까지 전파되고 같은 변환 로직이 화면마다 중복될 수 있었습니다.

판단: API 호출(api)/오케스트레이션(service)/형태 변환(dto)/타입(types)을 분리하면 결합도를 낮추고 변환을 재사용할 수 있다고 판단했습니다. 구직자 도메인 백엔드(NestJS)를 직접 담당했기에 API 계약을 프론트 4계층과 함께 일관되게 잡을 수 있었습니다.

액션: JobListDTO 등에서 API형을 화면형으로 변환(type→upjong 등)하고 service가 페이지네이션 메타와 함께 반환하도록 표준화했으며, 공통 코드는 앱 초기화 시 1회 로드해 Recoil atom에 캐싱해 50여 개 화면의 반복 호출을 제거했습니다.

결과: 동일 DTO를 대시보드·공고리스트·찜목록이 공유하고, 백엔드 필드 변경 시 DTO(또는 API)만 수정하면 되는 구조를 확보했습니다.

Challenge 3 | Zod refine + Sentinel 패턴으로 복잡한 이력서 폼 검증

상황: 이력서 폼은 “요일을 고르거나 ‘협의 가능’을 켜야 한다” 같은 교차필드 규칙과, 경력·자격증처럼 개수가 유동적인 카드 배열을 동시에 다뤄야 했습니다.

판단: 단순 스키마로는 XOR 조건을 표현할 수 없으니 Zod refine()으로 커스텀 규칙을 넣고, 동적 배열은 Sentinel(placeholder)로 최소 1카드를 보장하되 제출 시 걸러내면 UX와 데이터 정합을 모두 잡을 수 있다고 판단했습니다.

액션: refine 규칙을 요일·시간·급여·지역·업종에 적용하고 제출 DTO에서 Sentinel 카드를 필터링했으며, 폼의 Date 객체와 API의 ‘yyyy-MM-dd’ 문자열을 DTO가 양방향 변환하도록 했습니다.

결과: 빈 객체가 API로 전송되지 않으면서 폼은 항상 편집 가능한 카드를 유지해 입력 경험을 개선했습니다.

What I Learned | 배운 점

Allwage는 구직자·사업주 양면 사용자를 하나의 코드베이스로 다루고, Next.js 웹앱을 Expo(React Native) WebView로 감싸 iOS/Android 앱으로 배포하며, 구직자 도메인을 프론트엔드부터 NestJS·MySQL 백엔드까지 담당한, 역할별 2인 협업 풀스택 프로젝트입니다.

역할 기반 분기를 세션 플래그→유틸→진입점/컴포넌트 스왑으로 계층화하면 한 코드베이스에서 다중 사용자 여정을 산재된 조건문 없이 유지할 수 있음을 체감했고, 4계층+DTO·공통 코드 캐싱은 화면이 늘어날수록 재사용·유지보수 효과가 커지는 확장형 프론트엔드 표준 구조였습니다.