커버 이미지
프로젝트 로고

무빙

고객과 이사업체를 연결하여 여러 이사 견적의 가격과 조건을 한곳에서 비교할 수 있는 이사 견적 비교 플랫폼

작업 기간

2026년 07월 20일 ~ 2026년 08월 31일

기여도

40%

수상 내역

  • 🏆 코드잇 스프린트 Fullstack Finale 프로젝트 임팩트상

프로젝트 소개

  • 무빙은 고객이 여러 이사업체에 직접 연락하여 견적을 받아야 하는 기존 이사 준비 과정의 불편함을 해결하기 위한 이사 견적 비교 서비스입니다.
  • 고객이 이사 정보를 등록하면 이사업체가 견적을 제안하고, 고객은 가격과 조건, 실제 이용자의 리뷰를 비교하여 자신에게 적합한 업체를 선택할 수 있습니다.
  • 고객과 기사 역할에 따른 인증·인가, 프로필 관리, 견적 비교 및 리뷰 기능을 통해 이사 준비 과정을 보다 편리하게 이용할 수 있도록 설계되었습니다.

기술 스택

Frontend
  • React 로고React
  • Next.js 로고Next.js
  • Axios 로고Axios
Backend
  • Express 로고Express
  • Prisma 로고Prisma
Cloud
  • AWS S3 로고AWS S3
Collaboration
  • GitHub 로고GitHub
  • Notion 로고Notion

주요 기능

  • 일반 유저·기사님 유형별 회원가입 및 소셜 로그인 기능
  • 사용자 유형별 프로필 등록·수정 및 권한 관리 기능
  • 채팅형 이사 정보 입력 및 견적 요청 기능
  • 기사님 검색·필터링·정렬 및 찜하기 기능
  • 일반·지정 견적 요청 및 견적서 발송·반려 기능
  • 견적 비교·조회 및 이사 예약 확정 기능
  • 이사 이용 내역 조회 및 기사님 리뷰·평점 관리 기능
  • 견적 요청·확정 및 이사 일정 알림 기능

담당 기능

  • 1. JWT 인증·인가 및 토큰 갱신 처리

    JWT 인증·인가 및 토큰 갱신 처리 시연 이미지
    • -로그인 상태를 유지하면서도 Access Token의 유효 기간을 짧게 가져가기 위해 Access Token과 Refresh Token을 분리하고, Refresh Token Rotation 적용
    • -토큰 만료 시 사용자가 다시 로그인하지 않아도 요청을 이어갈 수 있도록 401 응답에서 토큰 갱신 후 기존 요청을 1회 재시도하도록 구현
    • -여러 API 요청에서 동시에 401이 발생하면 재발급 요청이 중복될 수 있어 inflightRefresh Promise를 공유하는 방식으로 처리
    • -localStorage의 사용자 정보는 임의로 변경할 수 있어 /api/auth/me 응답을 기준으로 인증 상태를 관리하고, 서버 미들웨어에서 역할과 계정 상태 검증
  • 2. Next.js 인증 헤더 깜박임 문제 해결

    Next.js 인증 헤더 깜박임 문제 해결 시연 이미지
    • -Next.js 환경에서 서버가 localStorage의 인증 정보를 확인할 수 없어 새로고침 시 비로그인 헤더가 일시적으로 노출되는 문제 발견
    • -로그인 여부를 확인하기 전 상태와 실제 비로그인 상태를 구분하고, 인증 상태 확인 전에는 Skeleton UI를 표시하도록 개선
    • -useSyncExternalStore를 활용해 서버와 클라이언트의 초기 렌더링을 일치시키고, 이후 localStorage의 인증 정보를 반영하도록 처리
    • -새로고침 시 비로그인 헤더가 잠깐 나타나는 현상을 해결하여 일관된 인증 UI 제공
  • 3. S3 Presigned URL을 이용한 프로필 이미지 업로드

    S3 Presigned URL을 이용한 프로필 이미지 업로드 시연 이미지
    • -이미지 업로드 시 Express 서버가 파일을 직접 중계하는 부담을 줄이기 위해 S3 Presigned URL 방식 선택
    • -서버에서 5분간 유효한 업로드 URL과 s3Key를 발급하고, 클라이언트가 S3에 직접 업로드한 뒤 해당 키를 프로필 API에 전달
    • -업로드에는 Presigned PUT을 사용하되, 조회는 파일의 공개 여부에 따라 구분하여 프로필·게시글 이미지는 공개 URL, 채팅 첨부파일은 Presigned GET 사용
    • -react-easy-crop을 활용해 이미지 크롭 기능을 구현하고, 업로드 전 파일 형식과 용량 검증
  • 4. 프로필 수정 시 S3 이미지 정리 처리

    프로필 수정 시 S3 이미지 정리 처리 시연 이미지
    • -S3 파일 업로드와 DB 저장은 하나의 트랜잭션으로 처리할 수 없어, 프로필 저장에 실패하면 사용하지 않는 이미지가 남는 문제를 고려
    • -DB 저장에 실패하면 새로 업로드한 이미지를 삭제하도록 처리해 불필요한 S3 객체가 남는 상황에 대응
    • -기존 이미지를 먼저 삭제하면 DB 수정 실패 시 이전 이미지까지 사라질 수 있어, DB 저장이 완료된 후 기존 이미지 삭제
    • -이미지 삭제 실패가 정상적으로 완료된 프로필 수정에 영향을 주지 않도록 오류 처리를 분리하고, 수정 후 최신 프로필 정보 조회

기술적 성과

  • 동시에 여러 요청에서 401 응답이 발생해도 토큰 재발급은 한 번만 요청하도록 처리
  • Next.js SSR 환경에서 인증 상태 확인 전 Skeleton UI를 표시해 새로고침 시 비로그인 헤더가 일시적으로 노출되는 현상 해결
  • S3 Presigned URL을 적용해 이미지 파일을 Express 서버를 거치지 않고 직접 업로드하도록 구현
  • 프로필 이미지 교체 시 DB 저장 결과에 따라 S3 객체를 정리해 불필요한 파일이 남는 상황에 대응

회고

  • 처음에는 localStorage에 저장된 사용자 정보를 기준으로 화면을 제어했지만, 해당 값을 변경하면 실제 권한과 화면이 일치하지 않는 문제가 있었다. 이를 해결하기 위해 /api/auth/me 응답을 기준으로 사용자 상태를 관리하도록 수정하고, 서버의 권한 검증과 프론트엔드의 화면 접근 제어를 분리했다.
  • Next.js 환경에서 서버는 localStorage의 인증 정보를 확인할 수 없어 새로고침 시 로그인 사용자에게 비로그인 헤더가 잠깐 표시되는 문제가 있었다. 이를 해결하기 위해 인증 상태 확인 전과 실제 비로그인 상태를 구분하고, useSyncExternalStore를 활용해 서버와 클라이언트의 초기 렌더링을 일치시켰다. 인증 상태가 확인되기 전에는 Skeleton UI를 표시하도록 처리하면서 SSR 환경에서 초기 렌더링과 인증 상태 관리의 중요성을 배웠다.
  • S3에 이미지를 업로드한 뒤 DB 저장에 실패하면 사용하지 않는 파일이 남을 수 있었다. 이를 방지하기 위해 DB 저장 실패 시 새로 업로드한 이미지를 삭제하고, 기존 이미지는 DB 수정이 완료된 후 삭제하도록 처리했다.
  • 처음에는 S3 이미지를 다룰 때 Presigned URL을 사용하는 것이 당연하다고 생각했다. 하지만 모의 면접을 계기로 다시 공부하면서 업로드와 조회는 목적이 다르고, 공개 이미지 조회에는 Presigned GET이 불필요할 수 있다는 점을 알게 됐다.
skill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badgeskill badge