추가 기능 확장 제안
북스토어 MVP 이후 인증·검색 필터·리뷰·추천·SEO·자동 배포 후보를 사용자 가치와 운영 비용으로 우선순위화합니다.
이전 절들에서 우리는 Next.js로 온라인 북스토어의 핵심 기능을 기획하고 설계하고 구현하는 과정을 살펴봤습니다.
MVP(Minimum Viable Product)를 배포했다면, 이제는 사용자 경험과 서비스 가치를 높일 추가 기능 확장 단계입니다.
이 절에서는 프로젝트 잠재력을 더 끌어올릴 수 있도록 확장 기능 아이디어와 구현 방향을 함께 제시합니다.
사용자 인증 및 권한 관리
17장 2절의 코드는 Auth.js 로그인과 세션의 공급자 계정 ID로 장바구니·주문 조회를 제한합니다. 이것은 제시한 코드의 범위이며 실제 배포·외부 연동·동시 요청 검증을 마쳤다는 뜻은 아닙니다.
이제 계정 정보, 역할, 프로필을 데이터베이스 모델과 연결해 인증을 실제 서비스 수준으로 확장할 차례입니다.
-
로그인 및 계정 확장
- Auth.js: 17장 2절에서 Next.js 프로젝트에 OAuth 공급자와 세션을 통합했습니다.
-
구현 고려사항
- OAuth 계정과 애플리케이션 사용자 모델을 연결.
- 이메일·비밀번호 가입이 필요하다면 비밀번호 해시와 계정 복구 흐름을 별도로 설계.
- 로그인 페이지에 공급자별 오류와 재시도 UI 추가.
- 필요한 클라이언트 UI만
SessionProvider안에 두고useSession으로 세션을 읽기. auth()를 이용한 서버 컴포넌트의 세션 정보 접근.
-
사용자 프로필 페이지
- 사용자가 자신의 주문 내역, 위시리스트, 개인 정보 등을 확인할 수 있는 페이지를 제공합니다.
- 기술 스택: 서버 컴포넌트에서 사용자별 데이터 페칭, 클라이언트 컴포넌트에서 데이터 업데이트(Server Actions 활용).
-
관리자 페이지 및 권한 부여
- 도서 정보 추가/수정/삭제, 주문 관리 등 관리자만 접근할 수 있는 페이지를 구현합니다.
-
구현 고려사항
- 사용자 모델에
role필드를 추가하여 admin과 같은 역할을 부여. - Route Handler나 Server Action에서 요청 시 사용자
role을 확인하여 권한 없는 접근 차단. - Proxy(
proxy.ts)를 사용하여 특정 경로의 접근 권한을 확인하고 리다이렉션.
- 사용자 모델에
검색 및 필터링 기능 강화
현재는 제목과 저자만으로 검색이 가능합니다.
사용자가 원하는 책을 더 쉽게 찾을 수 있도록 검색 및 필터링 기능을 확장할 수 있습니다.
-
고급 검색 옵션
- 장르별 필터링: 도서 목록 페이지에 장르를 선택할 수 있는 체크박스나 드롭다운을 추가.
- 가격 범위 필터링: 최소/최대 가격을 입력하여 해당 범위 내의 도서만 표시.
- 정렬 기능: 가격(오름차순/내림차순), 최신순, 인기순 등으로 도서를 정렬할 수 있는 옵션 추가.
-
구현 고려사항
- 클라이언트에서는
useSearchParams로 현재 URL 쿼리를 읽고, 수정은Link나useRouter의 탐색으로 반영. 이 훅이 반환한 객체는 읽기 전용입니다. - 서버 컴포넌트에서 이 쿼리 파라미터를 기반으로 데이터베이스 쿼리를 동적으로 구성.
- MongoDB의
$match,$sort등의 집계 파이프라인 연산자를 활용.
- 클라이언트에서는
-
검색어 자동 완성 (Autocomplete)
- 사용자가 검색어를 입력할 때 실시간으로 관련 추천 검색어를 제공하여 사용자 편의성 증대.
- 기술 스택: 클라이언트 컴포넌트(
useState,useEffect, 디바운싱), Route Handler(검색어 기반 데이터베이스 쿼리 또는 캐시된 검색어 목록).
가격 범위는 문자열 입력부터 검증하기 URL의 min·max는 문자열이고 사용자가 직접 바꿀 수 있습니다. 다음 순수 함수는 원 단위의 음수가 아닌 안전한 정수만 허용합니다. 누락된 최솟값은 0, 누락된 최댓값은 Number.MAX_SAFE_INTEGER로 두고, 빈 값·중복 파라미터·소수·역전된 범위는 거절하는 예제 정책입니다.
export function parsePriceRange(params: URLSearchParams) {
function read(key: string, fallback: number): number | null {
const values = params.getAll(key);
if (values.length === 0) return fallback;
if (values.length !== 1 || !/^\d+$/.test(values[0])) return null;
const value = Number(values[0]);
return Number.isSafeInteger(value) ? value : null;
}
const min = read('min', 0);
const max = read('max', Number.MAX_SAFE_INTEGER);
return min === null || max === null || min > max ? null : { min, max };
}min=10000&max=20000은 그 양 끝을 포함하는 범위 조건으로 사용할 수 있습니다. null이면 사용자에게 조건 오류를 알리고 DB 검색을 수행하지 않는 흐름을 설계합니다. 서버 Page는 자신의 searchParams 입력을 읽고 검증하며 Client hook을 호출하지 않습니다. 위 함수는 별도 URLSearchParams 객체의 입력 계약 예제입니다. 정렬값도 허용한 선택지를 고르고 DB 쿼리에 전달해야 하며 임의의 요청 문자열을 쿼리 코드로 실행하지 않습니다.
사용자 인터랙션 및 경험 개선
애플리케이션의 사용성을 높이고 더 풍부한 경험을 제공하는 기능들입니다.
-
위시리스트 (찜하기) 기능
- 사용자가 구매를 고려하는 도서를 저장해 둘 수 있는 기능을 제공합니다.
-
구현 고려사항
Wishlist모델을 생성하여userId와bookId를 연결.- 도서 상세 페이지에 위시리스트에 추가 버튼 구현 (Server Action).
- 사용자 프로필 페이지에 위시리스트 목록 페이지 구현.
-
도서 리뷰 및 평점 시스템
- 사용자가 구매한 도서에 대한 리뷰를 작성하고 평점을 매길 수 있도록 합니다.
-
구현 고려사항
Review모델 생성 (작성자userId,bookId,rating,comment).- 도서 상세 페이지에 리뷰 작성 폼 및 기존 리뷰 목록 표시.
- 리뷰 작성 시 Server Action으로 데이터베이스 업데이트.
- 각 도서의 평균 평점 계산 및 표시.
- 서버에서 검증한 작성자 ID와 구매 이력을 확인하고 평점을 1~5의 정수로 제한합니다. 요청 본문의 userId만으로 작성자를 정하지 않습니다. 리뷰가 없을 때는 아직 평점 없음으로 표시할 수 있고, 집단 평균을 다시 단순 평균하지 말고 전체 평점 합과 건수를 사용합니다.
-
실시간 재고 알림 또는 푸시 알림
- 품절된 도서가 입고되거나, 위시리스트에 담은 도서의 가격이 변동될 때 사용자에게 알림을 보냅니다.
-
기술 스택
- 푸시 알림: 직접 등록한 서비스 워커에서 Web Push API를 처리하고, 백엔드 서버는 VAPID 키로 구독 대상에 메시지를 전송. (16장 2절 PWA 설정 및 16장 4절 WebSocket 통합 참조)
- WebSocket: 실시간 재고 변동, 가격 업데이트 등을 사용자에게 즉시 반영. (별도의 WebSocket 서버 필요)
-
결제 시스템 연동
- 실제로 결제를 처리할 수 있도록 Stripe, Toss Payments, Iamport 등 PG사(Payment Gateway) API를 연동합니다.
-
구현 고려사항
- 클라이언트 측에서 결제 UI(카드 정보 입력 등) 처리.
- Route Handler에서 결제 요청 생성과 승인을 처리해 민감한 정보를 클라이언트에 노출하지 않습니다.
- 서버가 확인한 결제 결과로 주문 상태를 갱신하고, 중복 알림·재시도에서도 결제와 재고 변경이 중복되지 않도록 처리. 현재 가상 주문은 이미 재고를 차감하므로 실제 결제 흐름에 그대로 덧붙여 두 번 차감하지 않도록 상태 전이를 다시 설계.
결제 성공·중복 처리·동시성을 따로 확인하기 결제 완료 웹훅은 서비스가 보낸 요청인지 공급자의 공식 검증 절차로 확인하고 주문 ID·금액·통화를 서버의 주문과 비교합니다. Stripe의 경우 서명 검증에 원본 요청 본문이 필요하므로 먼저 JSON으로 변형한 값을 그대로 서명 검증에 넘기지 않습니다. 같은 이벤트 ID뿐 아니라 같은 결제의 다른 이벤트가 와도 주문의 동일한 paid 전이와 재고 변경을 중복 실행하지 않아야 합니다.
예를 들어 재고 차감을 pending→paid 전이에 한 번만 결합하기로 설계했다면 두 성공 알림에도 100→98 한 번으로 끝납니다. 이때 기존 가상 주문 시점의 차감을 제거하거나 예약 정책으로 재설계했다는 전제가 필요합니다. 이미 98로 차감한 주문에 paid 전이를 붙여 다시 차감하면 여전히 중복입니다. 메모리 Set이나 확인 후 따로 쓰기만으로 여러 서버·동시 요청에서 한 번을 보장할 수 없으므로 영속 식별자·고유 제약·원자적 상태 변경을 포함한 저장 정책과 실패 복구를 정합니다.
성능 및 SEO 최적화 심화
기본적인 최적화를 넘어, 더 높은 성능과 검색 엔진 노출을 위한 고급 기법을 적용합니다.
-
Incremental Static Regeneration (ISR) 활용
- 인기 도서나 변경이 드문 상세 정보를 캐시하고 허용할 오래된 응답의 범위를 정합니다. 주문 액션에서는 가격과 재고를 서버에서 다시 확인합니다.
- 구현 고려사항:
cacheComponents를 사용하지 않는 모델에서는 정적 생성 조건을 갖춘 경로의revalidate를 설정.cacheComponents를 활성화했다면 해당 모델의use cache·수명 설정으로 설계하고, 한 줄의 세그먼트 설정을 그대로 혼용하지 않기.
-
SEO 최적화
- Metadata API: App Router에서는 정적
metadata객체나generateMetadata함수로 제목, 설명,og:image등을 설정합니다.next/head는 Pages Router에서 사용하는 방식입니다. - Schema.org 마크업: 도서, 상품, 리뷰 등 구조화된 데이터를 JSON-LD 형식으로 페이지에 삽입하여 검색 엔진이 콘텐츠를 더 잘 이해하도록 돕습니다.
- 사이트맵 및 robots.txt: 검색 엔진 크롤러가 웹사이트를 효율적으로 탐색하고 인덱싱할 수 있도록 사이트맵(
sitemap.xml)과robots.txt를 생성합니다. robots.txt는 접근 권한 검사나 비밀 URL 보호를 대신하지 않습니다.
- Metadata API: App Router에서는 정적
-
CDN (Content Delivery Network) 활용
- 이미지, CSS, JS 등 정적 자산을 CDN을 통해 제공하여 사용자에게 더 빠르게 전송합니다. Vercel은 기본적으로 CDN을 활용합니다.
테스트 및 배포 자동화
안정적인 서비스 운영을 위해 필수적인 단계입니다.
-
자동화된 테스트
- 단위 테스트 (Unit Test): Jest, React Testing Library를 사용하여 개별 컴포넌트나 유틸리티 함수의 동작을 검증합니다.
- 통합 테스트 (Integration Test): 여러 컴포넌트나 모듈이 함께 작동하는 방식을 검증합니다.
- E2E 테스트 (End-to-End Test): Cypress, Playwright 등을 사용하여 실제 사용자 시나리오를 바탕으로 전체 애플리케이션의 동작을 검증합니다.
-
CI/CD (Continuous Integration/Continuous Deployment)
- GitHub Actions, Vercel Integrations 등을 사용하여 코드가 리포지토리에 푸시될 때마다 자동으로 테스트를 실행하고, 성공 시 배포까지 이어지도록 자동화 파이프라인을 구축합니다.
이 추가 기능들은 온라인 북스토어 프로젝트를 더 실제 서비스에 가깝게 확장할 때 참고할 수 있는 항목입니다.
기능마다 선행 조건이 다릅니다. 프로필·리뷰에는 안정적인 계정과 소유권 검사가, 실제 결제에는 주문 상태·중복 처리·환불 정책이 먼저 필요합니다. 원하는 사용자 가치와 감당할 운영 부담을 확인한 뒤 한 흐름씩 확장합니다.
작은 완료 조건으로 확장 순서를 정하기 검색 필터는 유효한 범위·빈 결과·잘못된 조건을, 리뷰는 비구매자 거절·본인 수정·평점 범위를, 결제는 금액 불일치·중복·취소 후 재시도를 포함해 한 흐름의 완료 조건을 먼저 적습니다. 버튼이 보이고 단위 함수가 통과한 상태와 실제 인증·DB·공급자를 연결한 통합 성공을 구분합니다. 자동 배포도 필요한 검사가 성공했다는 근거를 전달하는 절차이며 외부 결제의 중복·환불 시나리오까지 자동으로 검증됐다는 뜻은 아닙니다.
확장 전에 실패 조건을 정하기 가상 주문에서 재고 100권을 2권 차감해 98권으로 만들었다고 합시다. 실제 결제를 추가하면서 같은 주문의 결제 완료 알림을 두 번 받았는데, 알림마다 다시 2권을 차감하면 재고는 얼마가 될까요? 기존 가상 주문의 처리까지 포함해 어디를 다시 설계해야 할까요?
결제 알림 횟수와 업무 변경 횟수
기존 차감 뒤 두 알림에서 각각 차감하면 98 → 96 → 94가 되어 한 주문의 수량 2를 세 번 반영합니다. 결제와 주문 상태 전이를 먼저 정하고, 같은 주문·결제의 중복 알림은 같은 업무 변경을 다시 실행하지 않도록 식별자와 서버 검증을 사용해야 합니다. 기존 차감을 유지할지 예약·결제 확정 경계로 바꿀지도 함께 결정합니다.
알림을 받았다는 사실만으로 클라이언트가 보낸 결제 성공 값을 신뢰하지 않습니다. 실패·중복·취소·환불을 각각 어떤 상태로 기록하고 사용자에게 무엇을 보여 줄지 정한 뒤 구현합니다. 새 기능의 화면을 추가하는 일과 기존 주문의 일관성을 지키는 일은 함께 검토해야 합니다.
추가 연습: 확장 기능의 입력·업무 조건
- 가격 함수에 빈 쿼리,
min=10000&max=20000,min=20000&max=10000,min=&max=20000,min=1&min=2를 넣습니다. 각 결과와 서버가 검색을 진행할 조건을 판단하세요.useSearchParams()의 반환값에 직접 set을 호출해 URL을 바꾸는 방식도 가능할까요? - 리뷰 2개는 각각 5점이고 다른 리뷰 3개는 각각 1점입니다. 전체 평균은 얼마인가요? 두 집단 평균의 단순 평균을 사용하면 왜 틀릴까요? 리뷰가 없는 책에 0점이라는 실제 평가가 있었다고 표시해도 될까요?
- 기존 생성 시 재고 차감은 제거했고, 검증된 pending→paid 전이에만 수량 2를 한 번 차감하도록 설계했습니다. 초기 재고 100에서 같은 결제의 성공 알림 2개를 받아도 상태 전이가 한 번만 적용된다면 최종 재고는 얼마인가요? 메모리 Set만 사용하거나 동시 요청 둘이 pending을 먼저 읽었을 때 어떤 문제가 남을까요?
- 리뷰 버튼과 평점 입력 UI는 완성됐지만 서버에서 구매 이력·작성자 권한 검사를 하지 않습니다. 비구매자와 다른 사람의 리뷰 수정 요청도 허용된다면 기능 완료라고 판단할 수 있을까요? 다음 수정과 검증의 우선순위를 구체적으로 제안하세요.
조건·경계·선행 작업의 풀이
- 빈 쿼리는
{ min: 0, max: Number.MAX_SAFE_INTEGER }, 정상 범위는{ min: 10000, max: 20000 }이며 나머지 세 경우는 null입니다. 역전·빈 값·중복 입력을 예제 정책에서 거절하기 때문입니다. 반환 범위에 양 끝을 포함하는 DB 조건을 적용하고 null에서는 조건 오류로 처리합니다. 읽기 전용 hook 객체를 직접 수정하지 않고 복사한 쿼리로 Link나 router 탐색을 구성합니다. - 전체 합은 2×5+3×1=13이고 건수는 5이므로 2.6점입니다. 집단 평균만 (5+1)/2=3으로 계산하면 서로 다른 리뷰 수를 무시합니다. 리뷰 없음은 평가가 없는 상태이며 실제 0점 리뷰와 구분해 아직 평점 없음·건수 0처럼 표현합니다.
- 98입니다. 주어진 정책은 한 주문의 상태 전이 한 번에만 차감하며 기존 생성 시 차감도 없다는 조건입니다. 메모리 Set은 재시작·다른 인스턴스에서 기록을 공유하지 않고, 읽기 후 쓰기를 따로 하면 두 요청이 모두 차감할 수 있습니다. DB에서 원자적 전이·재고 변경과 영속 중복 식별 정책을 적용하고 동시·재시도·부분 실패를 검증해야 합니다. 순서대로 알림 두 개를 계산하는 예만으로 그 통합이 증명되지는 않습니다.
- 완료가 아닙니다. 서버에서 검증한 계정으로 구매 이력·리뷰 소유권·평점 범위를 먼저 확인하고 거절 시 저장하지 않도록 합니다. 정상 구매자의 작성·본인 수정 성공과 비구매자·타인 수정·범위 밖 평점 거절을 각각 확인합니다. UI 확인과 순수 검증 함수 이후 실제 인증·DB 경계의 통합 검증을 이어가야 합니다.