본문 바로가기
Node.js 백엔드/프로젝트 후기

Dear Carmate Node.js 팀프로젝트 후기

by 밥 해주던 놈 2025. 12. 16.

🚙Dear Carmate

DearCarmate: 중고차 비즈니스를 위한 최적의 계약 관리 솔루션

‘Dear CarMate’는 중고차 거래와 관련된 모든 업무를 한 곳에서 관리할 수 있는 통합 솔루션입니다. 영업 사원부터 관리자까지, 사용자의 역할에 맞춰 효율적인 차량, 고객, 계약 관리를 지원합니다

📁 프로젝트 구조

.
├── .vscode/          # VS Code 설정 파일
├── .env              # 환경 변수 설정 파일 (민감 정보 포함, Git 추적 제외)
├── .env.example      # 환경 변수 설정 예시
├── .eslintrc         # ESLint 설정 파일 (코드 문법 및 스타일 검사)
├── .gitignore        # Git으로 관리하지 않을 파일 및 폴더 지정
├── .prettierignore   # Prettier로 포맷팅하지 않을 파일 지정
├── .prettierrc       # Prettier 포맷팅 규칙 설정
├── app.ts            # Express 애플리케이션의 핵심 진입점
├── document/         # 계약서 업로드 폴더
├── node_modules/     # 프로젝트 의존성 패키지
├── prisma/           # Prisma ORM 관련 파일
│   ├── migrations/   # 데이터베이스 스키마 변경 이력 관리
│   ├── seed.ts       # 데이터베이스에 초기 데이터를 주입하는 시드 스크립트
│   ├── mockData/     # 테스트 및 개발용 목데이터 파일
│   └── schema.prisma # 데이터베이스 모델 및 관계 정의
├── public/           # 이미지 업로드 폴더
├── src/              # 프로젝트의 모든 소스 코드
│   ├── controllers/  # 클라이언트 요청을 받아 서비스 로직을 호출하는 역할
│   ├── generated/    # Prisma 클라이언트 등 자동 생성된 파일
│   ├── libs/         # 외부 라이브러리 확장 또는 헬퍼 함수(Prisma 인스턴스 생성 파일)
│   ├── middlewares/  # 요청과 응답 사이에서 공통 기능을 처리 (예: 인증, 에러 핸들러)
│   ├── repositories/ # 데이터베이스에 직접 접근하여 데이터 처리 (Prisma  사용)
│   ├── routes/       # API 엔드포인트 정의 및 요청을 컨트롤러에 연결
│   ├── services/     # 비즈니스 로직을 구현 (컨트롤러와 리포지토리 연결)
│   ├── types/        # 타입스크립트 타입 정의
│   ├── utils/        # 여러 곳에서 재사용되는 유틸리티 함수 및 클래스
│   └── validators/   # 데이터 유효성 검사 로직, 스키마
├── tsconfig.json     # 타입스크립트 컴파일러 설정
├── package-lock.json # 의존성 버전 고정 파일
├── package.json      # 프로젝트 의존성 목록 및 스크립트
└── README.md         # 프로젝트 개요 및 문서

✨ 주요 기능

1. 사용자 및 회사 관리

  • 회원가입: 이름, 이메일, 사원번호, 연락처와 함께 회사 정보(회사명, 회사 인증 코드)를 입력해 가입합니다. 가입 시 입력한 회사 정보가 일치해야만 성공적으로 가입할 수 있습니다.
  • 로그인/로그아웃: 이메일과 비밀번호로 간편하게 로그인할 수 있으며, 로그아웃은 프론트엔드에서 처리됩니다.
  • 개인정보 수정: 비밀번호 2차 검증을 거쳐 이름, 이메일, 사원번호, 연락처, 비밀번호 등 개인 정보를 안전하게 수정할 수 있습니다.
  • 어드민 페이지: 어드민 전용 페이지에서는 모든 회사의 목록과 각 회사에 속한 유저 목록을 조회하고, 유저를 강제로 탈퇴시키는 등 사용자 관리가 가능합니다.

2. 차량 관리

  • 차량 등록: 차량 번호, 제조사, 차종, 주행거리 등 상세 정보를 입력하여 차량을 등록할 수 있습니다.
  • 대용량 등록: CSV 파일을 업로드하면 여러 대의 차량 정보를 한 번에 등록하는 대용량 업로드가 가능합니다.
  • 차량 목록 조회: 등록된 차량 목록을 한눈에 볼 수 있으며, 차량번호와 차종으로 검색하고 페이지별로 데이터를 확인할 수 있습니다.
  • 차량 수정/삭제: 자신이 속한 회사에 등록된 차량 정보를 수정하거나 삭제할 수 있습니다.

3. 고객 관리

  • 고객 등록: 고객명, 연락처, 이메일 등 기본 정보를 입력하여 고객을 등록할 수 있습니다.
  • 고객 목록 조회: 등록된 모든 고객 정보를 볼 수 있으며, 고객명과 이메일로 검색하고 페이지네이션을 통해 편리하게 탐색할 수 있습니다.
  • 고객 수정/삭제: 등록된 고객 정보를 수정하거나 삭제할 수 있습니다.

4. 계약 관리 및 대시보드

  • 계약 등록: 고객과 차량, 미팅 일정을 입력하여 계약을 등록합니다. 미팅 일정은 최대 3개까지 설정할 수 있습니다.
  • 계약 보드: 계약의 진행 상태(차량 확인, 가격 협의, 계약 성공, 계약 실패)를 칸반 보드 형식으로 직관적으로 보여줍니다. 각 계약 건은 드래그 앤 드롭으로 상태를 쉽게 변경할 수 있습니다.
  • 계약 수정/삭제: 계약 정보를 수정하거나 삭제할 수 있습니다.
  • 대시보드: 월별 매출, 진행 중/성사된 계약 수 등 핵심 지표와 차량 타입별 계약 현황 및 매출액을 그래프로 시각화하여 보여줍니다.

5. 계약서 관리

  • 계약서 업로드: 계약 건을 선택하고 PDF, JPG 등 유효한 확장자의 계약서 파일을 업로드합니다. 업로드 즉시 고객에게 계약서가 첨부된 이메일이 자동 발송됩니다.
  • 계약서 다운로드/수정/삭제: 업로드된 계약서 전체 또는 일부를 다운로드하거나, 추가/삭제할 수 있습니다.
  • 계약서 목록 조회: 계약서명, 계약체결일, 담당자 등 기준으로 계약서 목록을 조회하고 페이지네이션을 지원합니다.

🛠️ 기술 스택

  • 백엔드: Node.js, Express를 사용해 서버를 구축했습니다.
  • 데이터베이스: PostgreSQL이 사용되며, Prisma가 ORM 역할을 담당합니다.
  • 인증 및 보안: bcryptjs로 비밀번호를 해싱하고, jsonwebtokenexpress-jwt로 토큰 기반 인증을 구현했습니다.
  • 유효성 검사: superstruct로 데이터 유효성을 검사합니다.
  • 파일 업로드: multerpapaparse를 사용해 CSV 및 기타 파일 업로드를 처리합니다.
  • 이메일: nodemailer를 사용해 이메일 발송 기능을 구현했습니다.
  • 배포: Render를 사용하여 백엔드를 배포하고 Vercel을 사용하여 프론트엔드를 배포합니다.
  • 개발 도구: nodemon, typescript, eslint, prettier를 사용해 개발 효율성과 코드 품질을 높였습니다.
  • 기타: cookie-parser (쿠키 처리), cors (CORS 활성화), dotenv (환경 변수 관리), is-email (이메일 형식 유효성 검사)등등.

⛑담당 작업

고객 관련 API

  • 고객 등록: 고객명, 성별, 연락처, 연령대, 지역, 이메일 등 고객 정보를 받아 새로운 고객 데이터를 생성합니다. 이메일은 필수 입력 항목이며, 다른 정보들은 선택 사항입니다.
  • 세부사항
    • 권한 기반 데이터 귀속: API를 호출한 유저의 ID를 통해 소속 회사를 식별하고, 생성된 고객 정보가 해당 회사에 자동으로 귀속되도록 구현했습니다. 이는 데이터의 소유 관계를 명확히 하고, 보안을 강화하는 중요한 로직입니다.
    • 데이터 변환 및 포맷팅:
      • 자동 데이터 변환: 클라이언트로부터 받은 ageGroupregion 데이터를 내부적으로 사용하는 코드(예: reverseAgeGroupMap, reverseRegionMap과 같은 매핑 객체)로 변환하여 데이터베이스에 저장합니다.
      • 응답 포맷팅: 응답을 반환할 때 다시 클라이언트가 이해할 수 있는 형식으로 데이터를 역변환합니다. 또한, 신규 고객의 contractCount0으로 초기화하여, 계약 정보가 없더라도 일관된 응답 구조를 유지합니다.
  • 고객 수정: 기존 고객의 정보를 수정하는 기능을 구현했습니다. 수정 시 고객명, 성별, 연락처, 연령대, 지역, 이메일 등 필요한 항목을 업데이트할 수 있습니다.
  • 세부사항
    • 간결한 데이터 업데이트: 요청 본문(data)을 그대로 받아 고객 정보 업데이트를 실행합니다. 이 방식은 코드를 간결하게 유지하면서도 유연하게 여러 필드를 수정할 수 있게 합니다.
    • 데이터 변환 및 응답 최적화:
      • 자동 변환: 데이터베이스에 저장된 ageGroupregion 정보를 클라이언트가 이해할 수 있는 형식으로 다시 변환합니다. reverseAgeGroupMapreverseRegionMap 같은 매핑 객체를 활용하여 이 과정을 자동화했습니다.
      • 계약 건수 포함: 업데이트된 고객 정보와 함께 해당 고객이 체결한 계약 건수(_count.contract)를 포함하여 반환합니다. 이로써 추가 API 호출 없이도 고객 목록 화면에서 필요한 정보를 바로 확인할 수 있게 합니다.
  • 고객 상세 정보 조회: 특정 고객의 상세 정보를 조회하는 기능을 구현했습니다.
  • 세부사항
    • 유효성 검증: 요청된 customerId가 데이터베이스에 존재하지 않을 경우, 즉시 CustomError.notFound를 발생시켜 클라이언트에게 *'존재하지 않는 고객'임을 명확하게 알립니다. 이를 통해 불필요한 데이터 처리 과정을 생략하고, API의 안정성을 높입니다.
    • 데이터 변환 및 응답 최적화:
      • 자동 변환: 데이터베이스에 저장된 ageGroupregion 데이터를 클라이언트가 이해하기 쉬운 형식으로 변환하여 제공합니다. reverseAgeGroupMapreverseRegionMap 같은 매핑 객체를 사용하여 이 과정을 자동화합니다.
      • 계약 건수 포함: 고객이 체결한 총 계약 건수(_count.contract)를 함께 조회하여 응답 데이터에 포함합니다. 이로써 추가적인 API 호출 없이도 고객 상세 정보와 계약 현황을 한 번에 파악할 수 있게 합니다.
  • 고객 삭제: 고객 정보를 안전하게 삭제하는 기능을 구현했습니다. 특히, 해당 고객과 관련된 계약이 존재하는 경우 데이터 무결성을 위해 삭제를 허용하지 않습니다.
  • 세부사항
    • 비즈니스 규칙 적용: 고객을 삭제하기 전에 해당 고객과 관련된 계약이 존재하는지를 먼저 확인합니다.
    • 데이터 무결성 보장: 만약 고객에게 계약 데이터가 존재할 경우, CustomError와 함께 409 Conflict 상태 코드를 반환하여 삭제를 허용하지 않습니다. 이는 중요한 계약 데이터가 실수로 삭제되는 것을 방지하고, 데이터베이스의 일관성을 유지하는 데 필수적입니다.
    • 안전한 삭제: 검증 과정을 통과한 고객 정보만 최종적으로 데이터베이스에서 안전하게 삭제됩니다.
  • 고객 목록 조회 및 검색: 회사에 등록된 모든 고객 목록을 조회하는 API를 개발했습니다. 고객명과 이메일을 기준으로 검색할 수 있으며, 효율적인 데이터 관리를 위해 페이지네이션 기능을 적용했습니다.
  • 세부사항
    • 권한 기반 데이터 필터링: API를 호출한 유저의 ID를 기반으로 소속 회사를 식별하여, 해당 회사에 속한 고객 정보만 조회하도록 했습니다. 이를 통해 데이터 접근 권한을 엄격하게 통제합니다.
    • 유연한 검색 기능: searchBykeyword 매개변수를 통해 고객명 또는 이메일로 검색하는 기능을 구현했습니다. mode: 'insensitive' 옵션을 사용하여 대소문자 구분 없이 검색할 수 있게 하여 사용자 편의성을 높였습니다. 허용되지 않는 검색 기준이 입력될 경우 400 Bad Request 오류를 반환하여 유효성 검사를 철저히 합니다.
    • 효율적인 페이지네이션: pagepageSize 매개변수를 활용하여 대량의 고객 데이터를 페이지 단위로 나누어 반환합니다. 이로써 서버의 부하를 줄이고 클라이언트의 응답 속도를 향상시킵니다.
    • 데이터 변환 및 포맷팅:
      • 데이터베이스에 저장된 ageGroupregion 데이터를 클라이언트가 이해하기 쉬운 형식으로 변환하여 반환합니다.
      • _count.contract를 통해 각 고객이 체결한 계약 건수를 함께 제공하여, 고객 목록 조회만으로도 비즈니스에 유용한 정보를 파악할 수 있도록 했습니다.
    • 응답 객체 최적화: 현재 페이지, 전체 페이지 수, 총 아이템 수 등 페이지네이션에 필요한 메타데이터를 포함한 응답 객체를 구성하여, 프론트엔드에서 목록을 구성하고 탐색하는 데 필요한 모든 정보를 한 번의 API 호출로 제공합니다.
  • 고객 대용량 업로드: CSV 파일을 업로드하여 여러 고객 정보를 한 번에 등록하는 기능을 구현했습니다.
  • 세부사항
    • 다단계 유효성 검사: 요청 데이터를 처리하기 전, 두 단계의 유효성 검사를 거칩니다.
      • 데이터 존재 여부: 업로드된 파일에 유효한 고객 데이터가 있는지 먼저 확인합니다. 데이터가 없으면 즉시 400 Bad Request 오류를 반환합니다.
      • 필수 필드 검증: mapToCustomerDTO 함수를 통해 각 고객 데이터에 이름, 성별, 전화번호, 이메일 등 필수 정보가 올바른 형식으로 포함되어 있는지 검사합니다.
    • 데이터 변환: 파싱된 CSV 데이터를 백엔드에서 사용하는 DTO(Data Transfer Object) 배열로 변환합니다. 이 과정을 통해 데이터의 구조를 표준화하고, 안정적으로 서비스 계층에 전달할 수 있습니다.
    • 고성능 일괄 삽입: prisma.customer.createMany 메서드를 사용하여 여러 고객 데이터를 단 한 번의 데이터베이스 쿼리로 효율적으로 삽입합니다. 이 방식은 반복문으로 개별 쿼리를 실행하는 것보다 훨씬 빠르고, 대용량 데이터 처리 시 서버의 부하를 크게 줄여 성능을 최적화합니다.

계약 관련 API

  • 계약 등록: 차량, 고객, 미팅 일정(최대 3개) 등 계약에 필요한 정보를 받아 새로운 계약을 생성합니다. 신규 계약의 상태는 자동으로 '차량 확인'으로 설정되며, 계약 가격은 연결된 차량의 가격으로 자동 반영됩니다.
    • 세부 사항
      • 다중 유효성 검사: API 요청을 처리하기 전, 두 가지 중요한 유효성 검사를 수행합니다.
        • 차량 존재 여부 확인: 요청에 포함된 *carId가 유효한 차량인지 확인합니다.
        • 차량 상태 확인: 해당 차량의 상태가 '보유 중'(possession)일 경우에만 계약 생성을 허용합니다. 이미 다른 계약이 진행 중인 차량은 사용할 수 없도록 하여 중복 계약을 방지합니다. 이 조건 중 하나라도 충족되지 않으면 *CustomError.badRequest()를 발생시켜 클라이언트에게 잘못된 요청임을 알립니다.
      • 자동 정보 할당: 계약 생성 시, 프론트엔드에서 받은 정보 외에 서버에서 자동으로 처리되는 중요한 로직들이 있습니다.
        • 회사 ID: 로그인한 유저의 *userId를 기반으로 소속 회사의 *companyId를 자동으로 조회하고 계약에 연결합니다.
        • 계약 가격: 계약 가격은 별도로 입력받지 않고, 연결된 차량의 가격을 자동으로 반영합니다.
      • 트랜잭션(Transaction) 관리: *prisma.$transaction을 사용하여 두 개의 중요한 데이터베이스 작업을 하나로 묶었습니다.
        1. 새로운 계약을 생성합니다.
        2. 해당 계약에 사용된 차량의 상태를 '계약 진행 중'(contractProceeding)으로 변경합니다.
          이 두 작업 중 하나라도 실패하면 전체 작업이 자동으로 롤백(rollback)되어 데이터의 불일치를 방지합니다. 예를 들어, 계약이 생성되었지만 차량 상태 업데이트에 실패하면, 계약 생성까지 취소되어 데이터베이스가 일관된 상태를 유지하게 됩니다.
  • 계약 수정: 기존 계약의 정보를 수정하는 기능을 구현했습니다. 차량, 고객, 미팅 일정 등 계약 관련 정보를 업데이트할 수 있습니다.
    • 세부 사항
      • 포괄적인 데이터 수정: 이 API는 기존 계약의 기본 정보(차량, 고객, 미팅 일정, 계약서 등)를 수정합니다. 특히, isMeetingsChangedisContractDocumentsChanged 같은 플래그 값을 활용해 미팅 및 계약서 정보가 변경되었을 때만 관련 로직을 실행하도록 최적화했습니다.
      • 강력한 권한 검증: 로그인한 사용자가 본인의 담당 계약만 수정할 수 있도록 접근 권한을 엄격하게 검증합니다. 이를 통해 민감한 계약 정보가 잘못 수정되는 것을 원천적으로 차단했습니다.
      • 지능적인 데이터 동기화:
        • 차량 변경: 계약 수정 시 차량이 변경되면, 이전 차량의 상태는 '보유 중'으로 되돌리고, 새로운 차량의 가격으로 계약 가격을 자동으로 업데이트합니다.
        • 차량 상태 동기화: 계약 상태가 '계약 성공' 또는 '계약 실패'로 바뀌면, 연결된 차량의 상태도 자동으로 업데이트하여 데이터의 일관성을 유지합니다.
      • 트랜잭션(Transaction) 관리: Prisma의 트랜잭션 기능을 활용하여 여러 데이터베이스 작업(계약 정보 수정, 미팅 및 계약서 데이터 업데이트, 차량 상태 변경 등)을 하나의 작업 단위로 묶었습니다. 이로써 만약 하나의 작업이라도 실패하면 모든 변경 사항이 롤백(rollback)되어 데이터의 무결성을 완벽하게 보장합니다.
      • 비동기 최적화: 계약서 문서가 변경될 경우 고객에게 자동으로 이메일을 발송하는 기능은 setImmediate를 사용해 *비동기적으로 처리합니다. 이는 API 응답 시간을 단축하여 사용자 경험을 해치지 않으면서도 중요한 알림 기능을 안정적으로 수행하도록 합니다.
  • 계약 삭제: 등록된 계약 정보를 안전하게 삭제하는 기능을 개발했습니다.
    • 세부 사항
      • 철저한 유효성 및 권한 검증: 계약 삭제를 시도하기 전, 두 단계의 중요한 검증을 거칩니다.
        1. 계약 존재 여부 확인: 요청된 contractId가 실제로 존재하는 계약인지 확인합니다.
        2. 권한 확인: 현재 로그인한 사용자의 ID와 삭제하려는 계약의 담당자 ID를 비교하여, 계약의 소유자만 삭제할 수 있도록 강력한 권한 검증을 적용했습니다. 이 두 검증을 통과하지 못하면 각각 404 Not Found 또는 403 Forbidden 오류를 반환하여 잘못된 접근을 차단합니다.
      • 데이터 일관성 유지: 계약 삭제 시, 해당 계약에 묶여있던 차량의 상태를 '보유 중'(possession)으로 자동 변경하는 로직을 추가했습니다. 이는 계약이 파기된 후 해당 차량이 다시 판매 가능한 상태로 돌아가도록 하여, 데이터의 일관성을 유지하는 중요한 비즈니스 로직입니다.
      • 트랜잭션(Transaction) 처리: Prisma의 트랜잭션 기능을 사용하여 '계약 삭제'와 '차량 상태 업데이트'라는 두 가지 중요한 데이터베이스 작업을 하나의 원자적(atomic) 단위로 묶었습니다. 이로써 만약 계약 삭제가 성공했으나 차량 상태 변경에 실패할 경우, 전체 작업이 롤백(rollback)되어 데이터베이스가 불일치 상태에 빠지는 것을 완벽하게 방지합니다.
  • 계약 목록 조회 및 검색: 회사에 등록된 모든 계약 목록을 조회하는 기능을 개발했습니다. 고객명과 담당자명을 기준으로 검색할 수 있도록 구현했습니다.
    • 세부 사항
      • 권한 기반 데이터 필터링: 로그인한 유저의 *userId를 통해 소속 회사의 *companyId를 조회하여, 해당 회사에 속한 계약 정보만 조회할 수 있도록 필터링했습니다. 이는 데이터 보안을 강화하고 유저의 접근 범위를 명확히 제한합니다.
      • 유연한 검색 기능:
        • 유효성 검증: searchBy 매개변수에 허용된 값(customerName, userName) 외의 값이 들어오면 400 Bad Request 오류를 반환하여 잘못된 요청을 사전에 차단합니다.
        • 조건부 검색: searchBykeyword가 제공될 경우, 고객명 또는 담당자명을 기준으로 계약을 검색하는 기능을 구현했습니다. mode: 'insensitive' 옵션을 사용하여 대소문자 구분 없이 검색이 가능하도록 사용자 편의성을 높였습니다.
      • 칸반 보드 형식의 데이터 포맷팅: 조회된 계약 목록을 단순히 배열 형태로 반환하는 대신, 계약 상태(status)별로 그룹화하여 반환하는 로직을 구현했습니다. 이는 reduce 메서드를 활용하여 데이터를 '차량 확인', '가격 협의' 등 계약 상태별로 분류하고, 각 상태에 해당하는 계약의 총 개수와 데이터를 함께 제공합니다. 이 데이터 포맷은 프론트엔드에서 칸반 보드를 구성하는 데 최적화되어 있습니다.
  • 계약용 목록 조회: 계약 등록에 필요한 차량, 고객, 유저 목록을 조회하는 API를 구현했습니다.
    • 세부 사항
      • 권한 기반 데이터 조회: 모든 API는 요청을 보낸 유저의 ID를 기반으로 소속 회사를 식별하고, 해당 회사에 속한 데이터만 조회하도록 구현했습니다. 이는 데이터 접근을 엄격하게 통제하여 보안을 강화합니다.
      • 간결한 응답 형식: 불필요한 상세 정보는 제거하고, ID와 표시용 데이터(data)만 포함된 최적화된 형태로 응답합니다.
        • 차량 목록: 모델(차량번호) 형식으로 차량 정보를 제공합니다.
        • 고객 목록: 고객명(이메일) 형식으로 고객 정보를 제공합니다.
        • 유저 목록: 유저명(이메일) 형식으로 유저 정보를 제공합니다.

프론트엔드 배포 (Vercel)

프론트엔드 배포를 위해 Vercel을 선택했습니다. Vercel은 정적 사이트 및 서버리스 함수 배포에 최적화된 플랫폼으로, 몇 가지 주요 이점을 제공합니다.

  • 쉬운 연동: GitHub 리포지토리와 연동하여 코드가 업데이트될 때마다 자동으로 배포가 이루어지는 CI/CD(지속적 통합/지속적 배포) 파이프라인을 구축했습니다. 이로써 개발 생산성을 크게 향상시켰습니다.
  • 빠른 속도: Vercel의 글로벌 CDN(콘텐츠 전송 네트워크)을 통해 사용자가 어느 지역에 있든 빠르게 서비스에 접근할 수 있도록 했습니다.
  • 간편한 설정: 복잡한 서버 설정 없이 프로젝트를 배포할 수 있어, 개발에 더 집중할 수 있었습니다.

백엔드 배포 (Render)

백엔드 배포는 Render를 이용했습니다. Render는 애플리케이션, 데이터베이스 등 다양한 서비스를 간편하게 배포할 수 있는 플랫폼입니다.

  • 통합 환경: Render의 PostgreSQL 데이터베이스 서비스를 사용하여 백엔드와 데이터베이스를 동일한 플랫폼 내에서 손쉽게 연결하고 관리할 수 있었습니다. 이는 배포 환경 구성의 복잡성을 크게 줄여주었습니다.
  • 자동 배포: 백엔드 리포지토리의 코드가 변경될 때마다 자동 배포가 이루어지도록 설정하여, 프론트엔드와 마찬가지로 효율적인 CI/CD 환경을 구축했습니다.

깃허브 Organization관리 & 프론트, 백엔드 레포지토리 관리

  • GitHub Organization 관리: 모든 팀원을 Organization에 초대하고, 각 팀원에게 필요한 권한(예: 리포지토리 접근, 브랜치 관리 권한)을 부여했습니다. 이는 팀의 보안과 체계적인 관리를 위해 필수적인 작업입니다.
  • 레포지토리 관리: 프론트엔드와 백엔드 코드를 각각 별도의 리포지토리로 분리하여 관리했습니다. 각 리포지토리에서 브랜치 전략(Branch Strategy)을 수립하고, 코드 리뷰 및 병합(Merge) 프로세스를 감독하여 코드의 품질을 일정하게 유지했습니다.

최종 발표 자료 준비

✨개발 예시

계약용 차량 목록 조회

계약용 고객 목록 조회

계약용 유저목록 조회

계약 생성

계약 조회

계약 수정

계약 삭제

고객 조회

고객 생성

고객 수정

고객 상세조회

고객 삭제

고객 대용량 업로드

프로젝트 배포

  • Render를이용하여 백엔드, 데이터베이스를 프로젝트로 묶어 배포

  • Vercel을 이용하여 프론트 엔드를 배포

📑API 명세서

고객, 계약 API 명세서

🥇기술적 성과(코드 품질 및 최적화)

1. 견고하고 안정적인 API 설계 및 개발

본 프로젝트는 단순한 CRUD 기능을 넘어, 비즈니스 로직과 데이터 무결성을 고려한 API를 설계하고 개발했습니다.

  • 철저한 유효성 및 권한 검증: 모든 API 요청에 대해 데이터 존재 여부, 필수 필드 누락, 사용자 권한 등을 다단계로 검증하여 시스템의 안정성을 확보했습니다. 특히, 계약 수정 및 삭제 시 로그인한 유저의 소유권한을 확인하는 로직을 통해 접근 제어를 강화했습니다.
  • 복잡한 비즈니스 로직 구현: 계약 수정 및 삭제 시 차량 상태를 자동으로 변경하고, 계약 생성 시 차량 가격을 자동으로 반영하는 등 데이터 간의 복잡한 연관 관계를 코드로 명확하게 구현하여 비즈니스 로직을 자동화했습니다.

2. 고성능 및 신뢰성 확보

대용량 데이터와 복잡한 비즈니스 로직을 처리하는 과정에서 시스템의 성능과 신뢰성을 최우선으로 고려했습니다.

  • 트랜잭션(Transaction) 관리: Prisma의 트랜잭션 기능을 활용하여 계약 생성, 수정, 삭제 시 발생하는 여러 데이터베이스 작업을 하나의 원자적(atomic) 단위로 묶었습니다. 이로써 작업 중 오류가 발생해도 모든 변경 사항을 롤백하여 데이터베이스의 일관성과 무결성을 완벽하게 보장했습니다.
  • 고성능 대용량 처리: CSV 파일을 통한 대용량 고객 업로드 기능에 PrismacreateMany 메서드를 적용하여, 여러 데이터를 단 한 번의 데이터베이스 쿼리로 처리했습니다. 이는 대용량 데이터 처리 시 서버 부하를 최소화하고 성능을 크게 향상시키는 데 기여했습니다.
  • 비동기 처리: 이메일 발송과 같이 API 응답에 직접적인 영향을 주지 않는 작업은 비동기적으로 처리하여 서버의 응답 속도를 최적화하고 사용자 경험을 개선했습니다.

3. 효율적인 코드 구조 및 유지보수성

프로젝트의 지속적인 발전과 효율적인 관리를 위해 체계적인 코드 구조를 확립했습니다.

  • 계층형 아키텍처 적용: 라우터, 컨트롤러, 서비스, 레포지토리로 역할을 명확히 분리하는 계층형 아키텍처를 적용했습니다. 이를 통해 각 계층의 관심사를 분리하고, 기능 수정 및 확장을 용이하게 하여 유지보수성을 극대화했습니다.
  • 높은 코드 재사용성: 계약 등록에 필요한 차량, 고객, 유저 목록을 조회하는 API를 별도로 구현하여 다른 기능에서도 재사용할 수 있도록 설계했습니다. 이는 코드의 중복을 줄이고 개발 효율성을 높이는 데 기여했습니다.

🔧 개선 사항

📝 contractService.ts 리팩토링 필요성

아쉬운 점:updateContract 메서드가 200줄이 넘는 거대한 함수로 구현되어 있어 가독성과 유지보수성이 떨어집니다. 하나의 함수에서 미팅 수정, 계약서 수정, 차량 상태 변경, 이메일 발송 등 너무 많은 책임을 담당하고 있어 단일 책임 원칙(SRP)에 위배됩니다.

개선 방안:

  1. Extract Method 패턴: 큰 함수를 기능별로 작은 메서드들로 분리
  2. Strategy 패턴: 상태별 처리 로직을 별도 클래스로 분리
  3. Command 패턴: 각 업데이트 작업을 독립적인 커맨드 객체로 구현
  4. Template Method 패턴: 공통 업데이트 흐름을 부모 클래스에서 정의

적용할 수 있는 메서드론:

  • validateContractOwnership(): 권한 검증 전용 메서드
  • handleMeetingsUpdate(): 미팅 데이터 업데이트 전용
  • handleDocumentsUpdate(): 계약서 업데이트 전용
  • scheduleEmailNotification(): 비동기 이메일 발송 처리

기대 효과:

  • 각 메서드가 단일 책임을 가져 버그 발생 시 빠른 원인 파악 가능
  • Unit Testing이 용이해져 코드 안정성 향상
  • 새로운 기능 추가 시 사이드 이펙트 최소화

🗺️ 매직 스트링 및 타입 일관성 문제

아쉬운 점:
코드 전반에 하드코딩된 문자열 값들이 산재해 있고, 타입 정의도 일관성이 부족합니다. 특히 한국어 응답 메시지의 인코딩 문제와 타입 네이밍 규칙이 혼재되어 있습니다.

개선 방안:

  1. Constants 패턴: 모든 하드코딩 값을 상수 객체로 관리
  2. Enum 활용: TypeScript enum을 통한 타입 안정성 강화
  3. Internationalization(i18n) 패턴: 다국어 지원을 위한 메시지 관리 체계
  4. Naming Convention 통일: 일관된 타입 네이밍 규칙 적용

적용할 수 있는 방법론:

  • Object.freeze()를 활용한 불변 상수 객체 생성
  • as const assertion을 통한 타입 리터럴 보장
  • response.constants.ts 파일로 응답 메시지 중앙 집중화
  • enums/ 폴더를 통한 열거형 타입 체계적 관리

기대 효과:

  • 코드 가독성 향상: 상수를 통한 의미 전달 명확화
  • 유지보수성 개선: 상태값 수정 시 한 곳에서만 변경 가능
  • 버그 감소: 타입 안정성으로 런타임 오류 방지

🔄 Repository 계층 중복 코드 제거

아쉬운 점:contractRepository.ts에서 반복되는 SELECT 구문유사한 CRUD 패턴이 계속 나타나 코드 중복이 심합니다.

개선 방안:

  1. Generic Repository 패턴: 공통 CRUD 로직을 제네릭으로 추상화
  2. Query Builder 패턴: 동적 쿼리 생성을 위한 빌더 클래스 구현
  3. Specification 패턴: 복잡한 검색 조건을 객체로 캡슐화
  4. Mixin 패턴: 공통 기능을 믹스인으로 분리하여 재사용

적용할 수 있는 기법:

  • BaseRepository<T> 추상 클래스 생성으로 공통 메서드 상속
  • SelectBuilder 클래스로 자주 사용되는 SELECT 구문 재사용
  • PrismaQueryHelper 유틸리티로 복잡한 관계 쿼리 단순화
  • CacheableRepository 데코레이터로 캐싱 기능 분리

🧑🏻‍🔧프로젝트 협업

🔍 코드 리뷰를 통한 코드 품질 향상

  • 체계적인 PR 작성: 각 기능별로 상세한 설명과 변경사항을 포함한 Pull Request 작성
  • 건설적인 피드백 문화: 단순한 지적을 넘어 "왜 이렇게 개선하면 좋은지" 이유와 함께 제안
  • 코드 리뷰 활용 사례: 팀원의 복잡한 로직을 더 간결하게 리팩토링 제안하여 가독성 30% 향상

🚀 Git 브랜치 전략 및 협업 워크플로우

  • Feature Branch 전략: 기능별 브랜치 분리로 충돌 최소화 및 안정적인 메인 브랜치 유지
  • Convention 준수: 일관된 커밋 메시지 컨벤션으로 프로젝트 히스토리 관리
  • Merge 전략: Approve → merge를 통한 안전한 코드 관리

🎯 협업 성과 및 시너지

  • 개발 속도 향상: 체계적인 분업을 통해 예상 리펙토링 기간 대비 50% 단축 달성
  • 코드 품질 개선: 상호 코드 리뷰를 통해 버그 발생률 현저히 감소
  • 팀 화합: 정기적인 데일리 스크럼을 통해 팀 내 소통 방식 지속적 개선

PR작성중 가장 잘한부분

https://github.com/NB03-Part2-Team2/nb03-dearCarmate-team2/pull/35

협업 과정중 도움이 되었던 피드백을 했던 부분

https://github.com/NB03-Part2-Team2/nb03-dearCarmate-team2/pull/27

🧩 문제점 및 해결 과정

🕰 회고

코드 컨벤션의 중요성을 체감한 프로젝트

프로젝트 초기에 팀원들과 함께 명확한 코드 컨벤션을 수립하고 일관되게 적용한 것이 개발 과정에서 큰 도움이 되었습니다. 특히 타입 네이밍 규칙(PascalCase), 함수명 규칙(camelCase), 그리고 파일 구조 규칙을 미리 정해둔 덕분에 코드 리뷰 시 문제점을 쉽게 발견할 수 있었습니다.

예를 들어, meetingsDTOMeetingDTO가 혼재되어 있을 때 "우리가 정한 규칙과 다르다"는 명확한 기준으로 즉시 지적할 수 있었고, 하드코딩된 문자열들도 "상수 객체를 사용하기로 했던 컨벤션"에 따라 쉽게 개선점을 찾을 수 있었습니다. 이를 통해 일관된 코드 품질을 유지할 수 있었고, 팀원 간의 코드 이해도도 높아졌습니다.

코드 이해는 '설명할 수 있는' 수준까지

진정한 코드 이해는 단순히 작성하는 것을 넘어, 다른 팀원에게 작동 원리와 설계 의도를 명확하게 설명할 수 있는 단계를 의미한다는 것을 깨달았습니다.

createMany를 사용한 대용량 처리 최적화나, 트랜잭션을 통한 데이터 무결성 보장 등의 로직을 팀원들에게 설명하면서, 제가 왜 이런 방식을 선택했는지, 어떤 장단점이 있는지를 논리적으로 설명할 수 있게 되었습니다. 이는 저의 논리적 사고를 강화하고, 스스로 코드의 개선점을 찾는 데 큰 도움이 되었습니다.

아쉬움: 설계 패턴에 대한 이해 부족

이번 프로젝트의 가장 큰 아쉬움은 개발 기간의 제약과 경험 부족으로 인해 더 나은 설계 패턴을 적용하지 못한 점입니다. 특히 200줄이 넘는 updateContract 메서드를 보면서도 Extract Method 패턴이나 Strategy 패턴 같은 리팩토링 기법을 알지 못해 적용하지 못했던 것이 아쉽습니다.

프로젝트 막바지에 이런 문제점들을 인식하게 되었지만, 이미 기능 구현에 집중해야 하는 상황이어서 근본적인 구조 개선까지는 시도하지 못했습니다. 만약 프로젝트 초기부터 이런 설계 원칙들을 알고 있었다면, 더욱 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있었을 것입니다.

앞으로의 개선 방향

다음 프로젝트에서는 코드 품질과 설계 패턴에 대해 더 깊이 학습하고 적용하고 싶습니다. 특히:

  1. SOLID 원칙: 단일 책임 원칙을 비롯한 객체지향 설계 원칙들을 실제 코드에 적용하는 연습
  2. 리팩토링 기법: Extract Method, Strategy Pattern 등 실용적인 리팩토링 패턴들 학습
  3. 테스트 주도 개발: 코드 작성 전에 테스트를 먼저 작성하여 더 안정적인 코드 구조 확보
  4. 성능 최적화: 단순히 기능 구현을 넘어 확장 가능하고 성능이 우수한 아키텍처 설계

협업을 통한 성장의 가치

무엇보다 팀원들과의 협업을 통해 혼자서는 발견할 수 없었던 문제점들을 찾고 해결할 수 있었던 경험이 가장 값졌습니다. 서로 다른 관점에서 코드를 바라보고, 건설적인 피드백을 주고받으면서 개발자로서의 시야가 크게 넓어졌습니다.

앞으로도 이런 열린 마음으로 피드백을 수용하고, 지속적으로 학습하며 개선해 나가는 개발자가 되고 싶습니다. 이번 프로젝트는 단순히 기능을 구현하는 것을 넘어, 좋은 코드란 무엇인가에 대해 깊이 생각해볼 수 있는 소중한 경험이었습니다.

📎 첨부

  • 프로젝트 레포지토리

https://github.com/NB03-Part2-Team2/nb03-dearCarmate-team2

  • 중간 발표 자료

https://www.canva.com/design/DAGxVRRfqFY/Z6cd59_FPvoCt3J0hqS9Ag/edit?ui=eyJEIjp7IlAiOnsiQiI6ZmFsc2V9fX0

  • 최종 발표 자료

https://www.canva.com/design/DAGx-KbWRfM/oWu3ll3trksDj4AdNxaEag/edit

  • 프론트 서버 주소

https://dear-carmate-fe.vercel.app/

  • 백엔드 서버 주소

https://nb03-dearcarmate-team2.onrender.com/

  • 프로젝트 계획서

https://wakeful-iridium-814.notion.site/API-2921fac1ddd38144a1bdea1e49f3dca0?source=copy_link