🍓 ReadyVery
간편 선결제 테이크아웃 서비스
"고객이 미리 주문/결제하고 매장에서 바로 픽업 또는 테이블에 주문 전달 가능"한 서비스를 목표로 개발
📋 프로젝트 개요
| 항목 | 내용 |
|---|---|
| 기간 | 2023.12 ~ 2024.05 (6개월) |
| 팀 구성 | 개발팀 BE 2명·FE 4명 / 협업 PM 1명·Designer 1명·Marketer 1명 |
| 역할 | 프론트엔드 개발 및 API 인터페이스 설계 |
| 배포 | AWS EC2 + Nginx |
🎯 주요 기능
고객 사이드
- 🔐 소셜 로그인: Kakao OAuth2 연동
- 🛒 선결제 테이크아웃: 메뉴 선택 → 결제 → 픽업
- 🍽️ 테이블 오더: QR 코드 스캔 → 주문 → 알림
- 💳 결제: Toss Payments SDK 연동 (카드/간편결제)
- 🎟️ 쿠폰/포인트: 할인 및 적립 시스템
사장님 사이드
- 📊 매장 관리: 정보 수정, 영업 시간 설정
- 🍔 메뉴 관리: 메뉴 등록/수정/삭제, 카테고리 관리
- 📦 주문 확인: 실시간 주문 알림, 상태 관리
- 💰 매출 통계: 일별/월별 매출 현황
🏗️ 구성
화면 구성

디렉토리 구조
// 사장님
src/
├── components/
│ ├── views/ # 공통 뷰
│ └── Sales/ # 매출 관리 차트
├── pages/
│ ├── OrderManage/
│ ├── ...
├── hooks/
├── Atom/
├── util/
└── constants/
// 고객
src/
├── components/
│ ├── Authentication/
│ └── views/
├── pages/
│ ├── HomePage/
│ ├── PaymentPage/
│ └── MyPage/
├── hooks/
├── Atom/
├── utils/
└── constants/
핵심 설계 원칙:
- 관심사 분리: UI 컴포넌트와 비즈니스 로직(Custom Hooks) 분리
- 재사용성 강화: Header, NavBar 등 공통 요소를 views/ 또는 components/ 하위로 분리
- 상태 관리 최적화: 전역(Recoil) / 서버(React Query) 상태 명확히 구분하여 불필요한 리랜더링 방지
ERD
- Customer ERD
- Owner ERD🔧 기술 스택
Frontend
| 기술 | 선택 이유 |
|---|---|
| React + Vite | 빠른 빌드 속도, HMR 지원으로 개발 생산성 향상 |
| Recoil | 간결한 API, 비동기 처리 용이, 작은 번들 사이즈 |
| React Query | 서버 상태 캐싱, 자동 재검증, 로딩/에러 상태 관리 |
| React Hook Form | 성능 최적화된 폼 처리, 유효성 검사 간소화 |
| Toss Payments SDK | 국내 환경 최적화 PG 연동 |
| Axios | Interceptor로 토큰/에러 중앙 관리 |
Backend
- Java + Spring Boot: 안정적인 서버 개발
- JPA + MySQL: 데이터 영속성 관리
- Redis: 세션 관리
🎯 담당 역할
1. Toss Payments SDK 통합 및 결제 시스템 구축
문제: 쿠폰·포인트 상태가 바뀔 때 Toss Payments Widget이 반복 초기화되며 로딩 지연과 화면 깜빡임 발생
해결: useEffect 최적화 + useRef 활용
// pages/PaymentPage.jsx
const PaymentPage = () => {
const paymentWidgetRef = useRef(null);
const paymentMethodsWidgetRef = useRef(null);
const paymentRequest = () => {
const paymentWidget = paymentWidgetRef.current;
const paymentMethodsWidget = paymentMethodsWidgetRef.current;
requestPayment(cartId, couponId, paymentWidget, paymentMethodsWidget, usedPoint);
};
// SDK 초기화를 금액 상태 변경과 분리
useEffect(() => {
(async () => {
const paymentWidget = await loadPaymentWidget(clientKey, customerKey);
paymentWidgetRef.current = paymentWidget;
const paymentMethodsWidget = paymentWidget.renderPaymentMethods(...);
paymentMethodsWidgetRef.current = paymentMethodsWidget;
})();
}, []); // 의존성 배열 비움 → 1회만 실행
return (
결제하기
);
};
// hooks/useRequestPayment
const useRequestPayment = () => {
const requestPayment = async (
cartId,
couponId,
paymentWidget,
paymentMethodsWidget,
point,
) => {
try {
// 1. 서버에 결제 정보 전송
const response = await api.post("/api/payments/prepare", {
cartId,
couponId,
point,
});
// 2. 서버가 계산한 최종 금액으로 위젯 업데이트
paymentMethodsWidget.updateAmount(Math.max(response.data.amount, 0));
// 3. Toss 결제창 호출
paymentWidget?.requestPayment(response.data);
} catch (error) {
console.error("결제 실패:", error);
}
};
return requestPayment;
};
핵심 개선
- ✅ SDK 초기화를 금액 관련 상태 변경과 분리
- ✅ 결제 버튼 클릭 시 서버 금액 기반으로 updateAmount 호출
- ✅ useRef로 SDK 인스턴스 값을 관리 → 불필요한 리렌더링 제거
2. 홈/마이페이지, 매장 관리 화면 전체 개발
- 마이페이지: 쿠폰/포인트/주문 내역 조회
- 매장 관리: 정보 관리, 메뉴 관리, 주문 확인 UI
3. Recoil 기반 전역 상태관리 및 React Query 데이터 페칭 최적화
// Atom 정의
export const cartState = atom({
key: "cartState",
default: [],
});
export const selectedCouponState = atom({
key: "selectedCouponState",
default: null,
});
// 서버 상태 관리 (React Query)
const useMenu = (storeId) => {
return useQuery({
queryKey: ["menu", storeId],
queryFn: () => api.get(`/api/stores/${storeId}/menu`),
staleTime: 1000 * 60 * 5, // 5분간 캐시 유지
});
};
🔥 핵심 기술 도전
결제 SDK의 반복 초기화 개선
문제: 결제 금액 상태 변경 시 Toss SDK 초기화와 금액 갱신 로직이 반복 실행
해결: useEffect 의존성 최적화 + useRef 활용
| 항목 | Before | After |
|---|---|---|
| SDK 초기화 | 금액 관련 상태 변경마다 재실행 | 컴포넌트 생명주기에 맞춰 실행 |
| 금액 갱신 | 별도 Effect에서 실행 | 결제 요청 시 서버 응답 후 실행 |
| 화면 깜빡임 | 반복 초기화 과정에서 발생 | 반복 초기화 제거 후 완화 |
📊 주요 성과
실사용 배포 및 운영
- ✅ 교내 축제에서 테이블 오더 서비스 운영
- ✅ 학교 인근 카페 2곳 실사용 배포 및 실제 운영
- ✅ 점주와 사용자 피드백을 반영해 주문 UI 개선
기술적 성과
- ✅ Toss SDK 초기화와 금액 갱신의 실행 시점 분리
- ✅ SDK 반복 초기화를 제거해 화면 깜빡임 완화
- ✅ Recoil + React Query로 전역/서버 상태 명확히 분리
💡 배운 점
비즈니스적 배움
- 기획–디자인–백엔드–마케팅과의 협업 과정을 통해, 서비스 전반을 바라보는 시야 확보
- 실제 점주 및 사용자 피드백을 반영하며, 비즈니스 관점에서 기능을 우선순위화하는 경험
기술적 배움
- useEffect 의존성 관리 중요성
- 외부 라이브러리는 React의 렌더링 사이클과 분리해서 관리
- 비동기 작업 순서 관리 경험
- SDK 인스턴스 초기화와 금액 갱신의 실행 순서가 결과에 영향을 줄 수 있음을 확인
- 결제 버튼 클릭을 금액 갱신의 단일 진입점으로 구성
- 백엔드 개발 전환 계기
- API 설계 과정에서 데이터 정합성, 트랜잭션 처리의 중요성 인식
- 결제 금액의 최종 검증과 주문 상태 변경을 서버가 책임지고, 클라이언트가 그 결과를 일관되게 표현하는 구조의 중요성을 체감
- 결제/주문 도메인의 복잡성을 이해하며 백엔드 개발에 대한 관심 증대