QRP는 ** QR 코드 기반 응급 의료정보 제공 서비스**입니다. 헬멧이나 안전모 혹은 차량에 부착된 QR 코드를 구조자나 의료진이 스캔하면, 로그인 없이도 응급처치에 필요한 핵심 의료정보와 긴급연락처를 빠르게 확인할 수 있습니다.
사고 현장에서는 환자의 상세 병력보다도 아래 정보가 먼저 필요합니다.
- 혈액형
- 알레르기
- 기저질환
- 복용 중인 약
- 긴급 연락처
QRP는 이 정보를 QR 스캔 한 번으로 즉시 조회할 수 있게 해, 응급 대응 시간을 줄이는 것을 목표로 합니다.
- QR 코드 등록 기반 회원가입
- 사용자 계정과 QR 코드를 연결
- 아이디 중복 확인 지원
- 3단계 의료정보 등록 플로우
- 계정 정보
- 개인 정보
- 건강 정보
- QR 기반 공개 의료정보 조회
/medical-info/:qrCode경로로 로그인 없이 접근 가능
- 로그인/로그아웃 및 세션 유지
- JWT를 HttpOnly 쿠키로 관리
- 의료정보 수정 기능
- 등록된 QR 코드 기준으로 정보 수정 가능
- 민감정보 암호화 저장
- 이름, 생년월일, 혈액형, 알레르기, 연락처 등 주요 필드를 DB 저장 시 암호화
- 모노레포 기반 공통 타입/스키마 공유
- 프론트엔드와 백엔드가
packages/shared의 Zod 스키마를 함께 사용
- 프론트엔드와 백엔드가
[사용자/구조자 브라우저]
|
v
[React + Vite Web App]
|
| REST API + Cookie Credentials
v
[NestJS API Server]
|
v
[MySQL]
- apps/web
- 모바일 우선 UI
- QR 등록, 로그인, 의료정보 조회/수정 화면 제공
- apps/api
- 사용자 등록, 인증, 공개 의료정보 조회 API 제공
- JWT 쿠키 인증과 DB 접근 담당
- packages/shared
- Zod 스키마와 DTO 타입 공유
- 프론트/백엔드 데이터 계약 일원화
- packages/ui
- 공통 UI 컴포넌트 패키지
- shadcn/ui 기반 디자인 시스템 역할
가독성을 위해 이 문서는 운영(Production) 아키텍처와 로컬 개발(Local Development) 아키텍처를 분리해서 설명합니다.
- 운영 환경에서는 Vercel + DNS + EC2(Nginx/PM2/NestJS/MySQL) 가 핵심입니다.
- 개발 환경에서는 Vite dev server +
/api프록시 가 핵심입니다.
- 사용자는 Vercel에 배포된 프론트엔드에 HTTPS로 접속합니다.
- 프론트엔드는 API 요청을
https://api.emergencyqr.cc로 보냅니다. - DNS가
api.emergencyqr.cc를 현재 EC2 IP(3.37.129.25)로 해석합니다. - EC2의 Nginx 가
Host: api.emergencyqr.cc기준으로 요청을 받습니다. - Nginx가 요청을
localhost:4000의 PM2-managed NestJS API 로 프록시합니다. - NestJS API는 필요 시 MySQL 과 통신합니다.
이 프로젝트를 이해할 때 가장 중요한 포인트는 도메인(host) 라우팅과 애플리케이션 path 라우팅이 분리되어 있다는 점입니다.
예: https://api.emergencyqr.cc/auth/me
- host:
api.emergencyqr.cc - path:
/auth/me
여기서 Nginx는 먼저 api.emergencyqr.cc 라는 호스트명으로 어떤 server block을 사용할지 결정합니다.
즉 api.emergencyqr.cc 는 URL path가 아니라 도메인/호스트 레벨입니다.
Nginx가 프록시한 뒤에는 NestJS가 실제 경로를 처리합니다. 현재 백엔드 컨트롤러 기준 path는 다음과 같습니다.
//auth/*/users/*
즉 현재 백엔드에는 global /api prefix가 없습니다.
[Developer Browser]
|
| requests /api/...
v
[Vite Dev Server :5173]
|
| proxy + rewrite
| /api/foo -> http://localhost:4000/foo
v
[NestJS API :4000]
|
v
[MySQL]
apps/web/vite.config.ts에서 개발 서버 프록시가 설정되어 있습니다.- 개발 환경에서는
/api/*요청이http://localhost:4000/*으로 프록시됩니다. - 이때 Vite가
rewrite: (path) => path.replace(/^\/api/, "")로/apiprefix를 제거한 뒤 백엔드로 전달합니다. - 프론트 axios 클라이언트(
apps/web/src/api/axios.ts)는 다음 우선순위로 API base URL을 결정합니다.import.meta.env.VITE_API_URL- 값이 없으면 기본값
"/api"
즉:
- 로컬 개발의 브라우저 요청 경로는
/api/...일 수 있고, - 실제 NestJS 서버 라우트는
/auth/...,/users/...처럼/api없이 동작합니다. - 이 차이를 메워주는 레이어가 바로 Vite dev proxy 입니다.
운영 배포에서는 Vite dev proxy가 존재하지 않으므로,
프론트는 VITE_API_URL 을 통해 API 도메인을 직접 바라보는 구성이 자연스럽습니다.
예:
VITE_API_URL=https://api.emergencyqr.cc
이 경우 브라우저는:
/api/auth/me가 아니라https://api.emergencyqr.cc/auth/me형태로 직접 호출하게 됩니다.
실제 서버에서 확인한 결과:
- 동작함:
/auth/me,/users/check-userid - 동작 안 함(404):
/api/auth/me,/api/users/check-userid
즉 현재 구현 기준으로는 브라우저 쪽 요청 경로는 /api 로 시작할 수 있지만, EC2 위의 NestJS 서버 자체 라우트는 /api prefix를 갖고 있지 않습니다.
- DNS resolution:
api.emergencyqr.cc -> 3.37.129.25 - Vercel config:
/home/ubuntu/QRP/apps/web/vercel.json - Frontend API client:
/home/ubuntu/QRP/apps/web/src/api/axios.ts - Vite proxy config:
/home/ubuntu/QRP/apps/web/vite.config.ts - Nginx site config:
/etc/nginx/sites-available/qrp - PM2 process:
qrp-api - API entry:
/home/ubuntu/QRP/apps/api/dist/main.js
- React 19
- Vite 7
- TypeScript
- React Router 7
- Zustand
- React Hook Form
- Zod
- Tailwind CSS 4
- shadcn/ui
@zxing/browser(QR 스캔)
- NestJS 11
- TypeScript
- TypeORM
- MySQL
- JWT
- cookie-parser
- bcrypt
- pnpm workspace
- Turborepo
- ESLint
- Prettier
QRP/
├─ apps/
│ ├─ web/ # React + Vite 프론트엔드
│ └─ api/ # NestJS 백엔드
├─ packages/
│ ├─ shared/ # 공통 스키마 / DTO / 타입
│ └─ ui/ # 공통 UI 컴포넌트
├─ package.json
├─ pnpm-workspace.yaml
└─ turbo.json
- 사용자가 회원가입 화면에서 QR 코드와 계정을 연결합니다.
- 개인 정보와 의료 정보를 입력합니다.
- 정보는 백엔드에서 검증 후 저장되며, 민감 필드는 암호화됩니다.
- 사고 발생 시 구조자가 QR 코드를 스캔합니다.
- 브라우저에서 해당 QR의 공개 의료정보 페이지가 열립니다.
- 구조자는 필요한 응급 정보를 즉시 확인합니다.
- Node.js 20+
- pnpm 10+
- MySQL
루트에서 실행:
pnpm installcp apps/api/.env.example apps/api/.env.developmentapps/api/.env.development 예시 항목:
NODE_ENV=development
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=
DB_NAME=qrp
JWT_SECRET=
JWT_EXPIRES_IN=1h
ENCRYPTION_SECRET_KEY=
CORS_ORIGIN=http://localhost:5173
FRONTEND_URL=http://localhost:5173cp apps/web/.env.example apps/web/.env.developmentapps/web/.env.example
VITE_API_URL=http://localhost:4000참고: 개발 환경에서는 Vite proxy가
/api -> http://localhost:4000으로 연결되도록 설정되어 있습니다.
MySQL에서 qrp 데이터베이스를 생성한 뒤,
백엔드 환경변수의 접속 정보를 맞춰주세요.
예시:
CREATE DATABASE qrp;루트에서 실행:
pnpm dev기본 동작:
- web:
http://localhost:5173 - api:
http://localhost:4000
pnpm dev
pnpm build
pnpm lint
pnpm format
pnpm typecheckpnpm --filter web dev
pnpm --filter web build
pnpm --filter web previewpnpm --filter api dev
pnpm --filter api build
pnpm --filter api testPOST /auth/loginPOST /auth/logoutGET /auth/me
POST /users— 회원가입GET /users/check-userid?userId=...— 아이디 중복 확인GET /users/medical-info/:qrCode— 공개 의료정보 조회PATCH /users/medical-info/:qrCode— 의료정보 수정
- 로그인 상태는 HttpOnly 쿠키 기반으로 관리됩니다.
- 의료정보 주요 필드는
ENCRYPTION_SECRET_KEY를 사용해 암호화 저장됩니다. - 운영 환경에서는 아래 항목을 반드시 점검해야 합니다.
- 강력한
JWT_SECRET - 안전한
ENCRYPTION_SECRET_KEY - 정확한
CORS_ORIGIN - HTTPS 환경에서의 쿠키 정책
- 강력한
- QR 코드 발급/관리 어드민 기능
- 구조자 전용 읽기 화면 UX 개선
- 의료정보 접근 로그/감사 기능
- 비상 연락 알림 연동
- 테스트 코드 및 배포 문서 보강
QRP는 사고 현장에서 QR 코드로 라이더의 핵심 의료정보를 빠르게 전달하기 위한 서비스입니다.