Tiny Market은 보안 코딩 과제용으로 구현한 작은 중고거래 플랫폼입니다.
단순히 상품을 등록하고 조회하는 기능을 넘어, 회원 인증, 권한 검증, 안전한 이미지 업로드, 대화/흥정 기록, 구매 확정 기반 정산 흐름, 관리자 검토 기능을 포함합니다.
- 회원가입 / 로그인 / 로그아웃
- 비밀번호 Argon2 해싱 저장
- 상품 등록, 검색, 상세 조회, 수정, 삭제
- 상품 이미지 파일 업로드
- 판매자 본인만 상품 관리 가능
- 상품 상태 관리
- 판매중
- 예약
- 판매 완료
- 차단
- 구매자가 상품을 구매하면 상품이 예약 상태로 변경되고 금액이 보류됨
- 구매자가 구매 확정하면 판매자 지갑으로 금액 정산
- 구매 취소 요청 시 판매자 승인 후 취소 처리
- 상품 문의 DM
- 흥정 가능 상품에 가격 제안 전송
- 판매자가 흥정 제안을 수락하면 상품 가격 변경
- 사용자 지갑 충전 요청
- 관리자의 충전 요청 승인 / 거절
- 관리자 페이지
- 사용자 관리
- 상품 관리
- 충전 요청 관리
- Next.js 16
- React 19
- TypeScript
- Prisma 7
- PostgreSQL
- NextAuth v4
- Zod
- Argon2
- CSS
src/
├── app/ # Next.js App Router 페이지와 API 라우트
├── generated/prisma/ # Prisma Client 생성 파일
├── lib/ # DB, 인증, 업로드, 검증 유틸
├── services/ # 구매, 송금 등 비즈니스 로직
└── types/ # NextAuth 타입 확장
prisma/
├── schema.prisma # DB 모델 정의
└── migrations/ # Prisma migration 파일
public/
└── uploads/products/ # 상품 이미지 업로드 저장 위치, Git 제외npm install프로젝트 루트에 .env 파일을 만들고 .env.example을 참고해 값을 설정합니다.
cp .env.example .env필수 환경 변수:
DATABASE_URL="postgresql://USER:PASSWORD@localhost:5432/DB_NAME?schema=public"
AUTH_SECRET="your-auth-secret"
NEXTAUTH_URL="http://localhost:3000"AUTH_SECRET은 아래 명령어로 생성할 수 있습니다.
openssl rand -base64 32주의: .env 파일에는 DB 비밀번호와 인증 시크릿이 들어가므로 Git에 올리면 안 됩니다.
로컬 PostgreSQL을 사용하거나 Docker로 PostgreSQL을 실행할 수 있습니다.
Docker를 사용할 경우 .env.docker.example을 복사해 .env.docker 파일을 만들고 값을 설정합니다.
cp .env.docker.example .env.docker.env.docker 예시:
POSTGRES_USER="tiny_market_user"
POSTGRES_PASSWORD="your_password"
POSTGRES_DB="tiny_market"주의: .env.docker에는 DB 비밀번호가 들어가므로 Git에 올리면 안 됩니다. 대신 .env.docker.example만 Git에 포함합니다.
Docker 실행 예시:
docker run --name tiny-market-postgres \
--env-file .env.docker \
-p 127.0.0.1:5432:5432 \
-v tiny-market-postgres-data:/var/lib/postgresql/data \
-d postgres:17-alpine위 값을 사용한다면 .env의 DATABASE_URL은 다음과 같이 설정할 수 있습니다.
DATABASE_URL="postgresql://tiny_market_user:your_password@localhost:5432/tiny_market?schema=public"Prisma migration을 적용합니다.
npx prisma migrate devPrisma Client를 생성합니다.
npx prisma generateDB 데이터를 확인하거나 테스트 계정을 수정하려면 Prisma Studio를 사용할 수 있습니다.
npx prisma studionpm run dev브라우저에서 아래 주소로 접속합니다.
http://localhost:3000npm run lint
npm run build/ 홈
/signup 회원가입
/login 로그인
/products 상품 목록 / 검색
/products/new 상품 등록
/products/[id] 상품 상세
/products/[id]/edit 상품 수정
/products/[id]/checkout 상품 구매 확인
/conversations 내 대화 목록
/conversations/[id] 대화 상세
/wallet/charge 지갑 충전 요청
/transfers/new 사용자 간 송금
/mypage 내 정보 / 지갑 / 구매 / 판매 내역
/admin 관리자 홈
/admin/users 사용자 관리
/admin/products 상품 관리
/admin/charges 충전 요청 관리초기에는 일반 회원가입으로 생성되는 사용자가 USER 역할을 가집니다.
관리자 페이지를 테스트하려면 Prisma Studio에서 특정 사용자의 role을 ADMIN으로 변경합니다.
npx prisma studio- 회원가입으로 테스트 계정 생성
- Prisma Studio 실행
User테이블에서 해당 사용자의role을ADMIN으로 변경/admin접속
- 일반 사용자로 회원가입
/wallet/charge에서 충전 요청 생성- 관리자 계정으로 로그인
/admin/charges에서 충전 요청 승인- 구매자 계정으로 상품 구매
- 상품은
예약상태가 되고 구매 금액은 보류 - 구매자가 마이페이지에서 구매 확정
- 판매자 지갑으로 금액 정산
취소 흐름:
- 구매자가 구매 취소 요청
- 판매자가 취소 요청 승인
- 구매자에게 금액 환불
- 상품은 다시 판매중 상태로 변경
상품 등록/수정 시 이미지는 URL 입력이 아니라 로컬 파일 업로드 방식으로 처리합니다.
- 허용 확장자:
png,jpg,jpeg - 허용 MIME 타입:
image/png,image/jpeg - 파일 시그니처 검사 수행
- 최대 파일 크기 제한
- 업로드 파일명은 원본 파일명을 그대로 쓰지 않고 UUID 기반으로 생성
- 업로드 파일은
public/uploads/products/에 저장 - 업로드 파일 디렉터리는 Git에 포함하지 않음
- NextAuth 세션 기반 로그인 사용
- 로그인한 사용자만 상품 등록, 대화, 구매, 충전 요청 가능
- 관리자 기능은
ADMIN역할만 접근 가능 - 관리자 페이지뿐 아니라 관리자 API에서도 권한 재확인
- 판매자 본인만 상품 수정, 삭제, 상태 변경 가능
- 대화 조회와 메시지 전송은 대화 참여자만 가능
- 비밀번호를 평문 저장하지 않음
- Argon2로 해싱 후
passwordHash만 저장 - 로그인 응답과 세션에
passwordHash를 포함하지 않음
- Zod를 사용해 회원가입, 로그인, 상품, 메시지, 송금, 충전 요청 입력값 검증
- 알 수 없는 필드는
.strict()로 거부 - 상품 가격, 메시지 길이, 송금 금액, 충전 금액 제한
- 클라이언트에서 전달된
sellerId,senderId등을 신뢰하지 않고 서버 세션과 DB 조회값 기준으로 처리
- 확장자만 믿지 않고 MIME 타입과 파일 시그니처를 함께 확인
- 허용된 이미지 형식만 저장
- 파일 크기 제한으로 과도한 업로드 방지
- 랜덤 파일명 사용으로 경로 추측과 파일명 충돌 가능성 감소
- 구매, 구매 확정, 구매 취소, 송금, 충전 승인 등 금액 변경 로직은 DB transaction으로 처리
- 구매 금액은 바로 판매자에게 지급하지 않고 구매 확정 전까지 보류
- 잔액 부족, 자기 상품 구매, 차단 상품 구매 방지
- 취소는 구매자 요청과 판매자 승인 절차를 거치도록 구현
- 차단된 사용자는 로그인 불가
- 차단된 상품은 일반 상품 목록과 상세 페이지에서 숨김
- 관리자는 사용자, 상품, 충전 요청을 확인하고 조치 가능
- 비밀번호 평문 저장 위험
- Argon2 해싱 저장으로 개선
- 클라이언트가
sellerId,senderId,buyerId를 조작할 위험- 서버 세션과 DB 조회값 기준으로 처리하도록 개선
- 다른 사용자의 상품/대화/구매 내역에 접근할 수 있는 IDOR 위험
- 상품 소유자, 대화 참여자, 구매자/판매자 확인 로직 추가
- 이미지 업로드 시 악성 파일이 저장될 위험
- MIME 타입, 확장자, 파일 시그니처, 크기 제한 검증 추가
- 차단 상품이 일반 사용자에게 노출될 위험
- 일반 상품 조회에서
BLOCKED상태 제외
- 일반 상품 조회에서
- 관리자 버튼 숨김만으로 권한을 처리하는 위험
- API와 서버 페이지에서 관리자 권한을 다시 확인
- 금액 처리 중 일부 작업만 성공할 위험
- transaction으로 잔액 변경과 거래 기록 생성을 묶음
- 새로운 API를 만들 때는 인증/인가 검사를 API 내부에서 반드시 수행합니다.
- 클라이언트 컴포넌트에서는 Prisma Client를 직접 import하지 않습니다.
- 민감정보는 응답에 포함하지 않고 필요한 필드만
select합니다. .env, DB 비밀번호, 인증 시크릿은 Git에 포함하지 않습니다.- 배포 환경에서는
AUTH_SECRET,DATABASE_URL,NEXTAUTH_URL을 별도로 설정해야 합니다.