1. 프로젝트 개요
- 최애의 포토는 사용자가 직접 포토카드를 생성하고, 마켓플레이스에서 포토카드를 구매하거나 교환할 수 있는 포토카드 거래 플랫폼입니다.
- 주요 기능은 랜딩 페이지, 회원가입/로그인, 마이갤러리, 포토카드 생성, 마켓플레이스, 판매 등록, 구매, 교환 제안, 알림, 랜덤 포인트 획득 등으로 구성되어 있습니다.
- 이번 프로젝트에서는 6명의 팀원이 FE와 BE를 함께 담당하는 풀스택 방식으로 개발을 진행했으며, 저는 주로 마켓플레이스의 구매/판매/교환 흐름과 관련된 API 설계 및 연동, BE 공통 구조 정리, 배포 과정의 문제 해결을 담당했습니다.
2. 담당한 작업
- 저는 프로젝트 초반 요구사항 분석 단계에서 유저 스토리, 소프트웨어 요구사항, ERD, Prisma Schema, API 명세서 작성을 함께 정리했습니다. 특히 포토카드 생성 수량 제한, 마이갤러리, 판매 등록, 구매, 교환 제안, 알림 정책처럼 여러 도메인이 연결되는 기능을 데이터 흐름 기준으로 구조화했습니다.
- BE에서는 Photocard, Ownership, ShopListing, Exchange 도메인 중심으로 Router, Controller, Service, Repository 계층 구조를 잡고, 각 계층의 역할을 분리했습니다. 또한 공통 응답 처리, 에러 처리, validation middleware, asyncHandler 같은 공통 미들웨어 구조도 함께 정리했습니다.
- FE에서는 마켓플레이스 상세 페이지 기준으로 판매자/구매자 분기, 구매 API 연동, 판매 수정/내리기 API 연동, 교환 제안 및 승인/거절 API 연동 방식을 정리했습니다. 이후 TanStack Query 기반으로 .api.js와 .queries.js 역할을 분리하여 서버 상태 관리 구조를 잡았습니다.
- 배포 단계에서는 FE는 Vercel, BE는 Render에 배포하면서 환경변수, CORS, 쿠키 인증, Prisma generate, migration, 빌드 에러 등을 해결했습니다.
3. 기술적 성과
- 가장 큰 성과는 복잡한 거래 흐름을 도메인 단위로 분리해 관리할 수 있는 구조를 만든 점입니다. 포토카드 자체 정보는 Photocard, 사용자의 보유 수량은 Ownership, 판매 등록 정보는 ShopListing, 교환 요청은 Exchange로 분리하여 각 테이블과 API의 책임을 명확히 했습니다.
- FE에서는 TanStack Query를 도입하여 구매, 판매, 교환 같은 서버 상태 변경 작업을 useMutation으로 관리하고, 성공 후 관련 query를 invalidate하는 방식으로 UI 동기화를 처리할 수 있게 했습니다. 덕분에 API 호출 코드와 화면 컴포넌트의 책임이 분리되어 유지보수성이 좋아졌습니다.
- 또한 Next.js 환경에서 NEXT_PUBLIC_API_URL, 이미지 도메인 설정, Suspense boundary, AuthProvider 기반 인증 상태 관리 등 배포 환경에서 필요한 설정을 직접 점검하고 수정했습니다.
- BE에서는 Render 배포 환경에 맞춰 npm start, prisma generate, prisma migrate deploy, DATABASE_URL, CLIENT_URL, JWT_SECRET 등의 설정을 정리했고, 로컬과 배포 환경 모두에서 API가 동작할 수 있도록 CORS 설정을 개선했습니다.
4. 문제점 및 해결 과정
[4-1: 배포 환경에서 API 요청이 실패하는 문제]
- 배포 후 프론트엔드에서 백엔드 API를 직접 호출할 때 CORS 오류가 발생했습니다. 로컬에서는 정상 동작하던 요청도 Vercel과 Render처럼 서로 다른 도메인에 배포되면 브라우저의 origin 정책에 의해 차단될 수 있었습니다.
- 기존에는 client.js에서 Render 백엔드 주소를 직접 BASE_URL로 사용했지만, 이후 BASE_URL을 /api로 변경하여 Next.js 내부 API 경로를 경유하도록 수정했습니다.
- 이를 통해 프론트엔드는 동일 origin인 /api로 요청을 보내고, 내부 프록시를 통해 백엔드와 통신하게 되었습니다. 그 결과 CORS 문제를 줄이고 배포 환경에서도 API 요청이 정상적으로 동작하도록 개선했습니다.
[4-2: 로그인 후 인증 상태가 UI에 반영되지 않는 문제]
- 로그인에 성공했음에도 GNB에는 계속 로그인/회원가입 버튼이 표시되는 문제가 있었습니다. 원인은 GNB가 실제 로그인 사용자 정보를 사용하지 않고 더미 유저 데이터를 기준으로 렌더링하고 있었기 때문입니다.
- 이를 해결하기 위해 AuthProvider에서 /auth/me 요청을 통해 현재 로그인한 사용자 정보를 가져오고, GNB는 user, isLoggedIn, isPending 상태를 기준으로 UI를 렌더링하도록 수정했습니다.
- 이후 로그인 상태에서는 닉네임, 포인트, 로그아웃 버튼이 표시되고, 비로그인 상태에서는 로그인/회원가입 버튼이 표시되도록 개선했습니다.
[4-3: Vercel 빌드 시 import 경로 오류 발생]
- 로컬에서는 정상적으로 실행되던 코드가 Vercel 배포 과정에서 빌드 실패가 발생했습니다. 원인은 컴포넌트 import 경로의 대소문자가 실제 폴더명과 일치하지 않았기 때문입니다.
- macOS 환경에서는 파일 경로의 대소문자 차이가 크게 드러나지 않을 수 있지만, Vercel의 Linux 환경에서는 대소문자를 엄격하게 구분합니다. 따라서 실제 폴더명과 import 경로를 정확히 일치시키는 방식으로 문제를 해결했습니다.
- 이 과정을 통해 배포 환경에서는 파일명, 폴더명, import 경로의 대소문자를 반드시 일치시켜야 한다는 점을 확인했습니다.
[4-4: Prisma Client가 초기화되지 않는 문제]
- Render 배포 후 서버 실행 중 @prisma/client did not initialize yet 오류가 발생했습니다. 원인은 배포 환경에서 Prisma Client 생성 과정이 실행되지 않았기 때문입니다.
- 이를 해결하기 위해 Render의 Build Command에 다음 명령어를 포함했습니다.
npm install && npx prisma generate && npx prisma migrate deploy
- prisma generate를 통해 Prisma Client를 생성하고, prisma migrate deploy를 통해 배포 환경 DB에 migration을 반영하도록 수정했습니다. 이후 배포 환경에서도 Prisma Client가 정상적으로 동작했습니다.
[4-5: 환경변수 및 Secret 관리 문제]
- .env.local에 GitHub Token, Google OAuth Secret 같은 민감 정보가 포함된 상태로 커밋되어 GitHub Push Protection에 의해 push가 차단되는 문제가 있었습니다.
- 단순히 파일을 삭제하는 것만으로는 이미 커밋 히스토리에 secret이 남아 있기 때문에 문제가 해결되지 않았습니다. 해당 커밋을 정리하고, .env.local은 git에 올라가지 않도록 관리했습니다.
- 이후 팀원들과 공유가 필요한 환경변수는 실제 값이 없는 .env.example 형태로 정리하는 방향으로 개선했습니다. 이를 통해 민감 정보가 저장소에 노출되는 위험을 줄였습니다.
5. 협업 및 피드백
- 프로젝트 초반에는 기능 범위가 넓고 요구사항이 복잡해서 담당 파트를 나누는 데 어려움이 있었습니다. 이를 해결하기 위해 페이지와 기능을 기준으로 팀원별 담당 범위를 다시 정리했습니다.
- 저는 마켓플레이스의 구매/판매/교환 흐름을 맡은 팀원들과 API 명세를 맞추고, 프론트엔드에서 필요한 요청/응답 데이터 구조를 백엔드 구현과 계속 비교했습니다. 특히 교환 제안, 교환 승인/거절, 판매 내리기처럼 상태 변경이 많은 기능은 FE와 BE의 책임을 명확히 나누는 것이 중요했습니다.
- 또한 팀원들이 작업한 컴포넌트를 최대한 건드리지 않고, 기존 컴포넌트 구조 위에 API 연동 로직을 얹는 방향으로 작업했습니다. 이를 통해 충돌을 줄이고, 각자 맡은 UI 작업을 유지하면서 기능을 연결할 수 있도록 했습니다.
- 코드 구조에 대해서도 피드백을 주고받았습니다. 예를 들어 validation middleware에서 body, params, query를 각각 따로 검사하던 방식은 휴먼 에러 가능성이 있어 공통 schema 기반으로 통합하는 방향을 논의했습니다. 이런 피드백을 반영하면서 팀 전체가 사용할 수 있는 일관된 백엔드 구조를 만들어갔습니다.
6. 향후 개선 사항 및 제안
- 현재 구조는 기본적인 기능 구현과 배포까지는 가능하지만, 앞으로 안정성을 높이기 위해 몇 가지 개선이 필요합니다.
- 먼저 인증 구조를 더 명확히 정리할 필요가 있습니다. 현재는 쿠키 기반 인증을 사용하고 있기 때문에 FE/BE 도메인이 다른 배포 환경에서 credentials: "include", CORS, cookie option 설정이 매우 중요합니다. 추후에는 refresh token 정책, access token 만료 처리, 로그인 유지 전략까지 함께 정리하면 더 안정적인 서비스가 될 것 같습니다.
- 두 번째로 에러 응답 형식을 더 일관되게 관리할 필요가 있습니다. AppError, ERROR_CODE, successResponse 구조는 이미 잡혀 있으므로, 모든 도메인에서 동일한 응답 형식을 사용하도록 통일하면 FE에서 에러 처리하기 훨씬 쉬워집니다.
- 세 번째로 API 테스트 환경이 필요합니다. 구매, 판매, 교환은 데이터 상태가 계속 바뀌는 기능이기 때문에 seed 데이터만으로는 테스트가 어려운 경우가 있습니다. Postman 또는 Swagger 예시 요청을 정리하고, 테스트용 계정/포토카드/판매글/교환 제안을 미리 구성하면 QA 속도가 빨라질 것입니다.
- 마지막으로 배포 환경 문서화가 필요합니다. Vercel과 Render에 어떤 환경변수를 넣어야 하는지, migration은 언제 실행해야 하는지, CORS에는 어떤 URL을 넣어야 하는지 등을 README에 정리하면 팀원들이 같은 문제를 반복해서 겪지 않을 수 있습니다.