상품 상세 페이지에 최근 본 상품 기능 추가
목차
상품 상세 페이지에 최근 본 상품 기능을 붙였다.
왜 이걸 지금 했나
이커머스 계열 서비스라면 "최근 본 상품" 은 UX 관점에서 거의 기본 중의 기본이다. 사용자가 이것저것 둘러보다가 다시 돌아오고 싶을 때, 뒤로가기를 연타하거나 검색을 다시 하는 건 불필요한 마찰이다. 최근 본 상품 목록이 있으면 그 마찰을 없애준다.
문제는 이걸 어디에 저장하냐는 거다. 서버에 저장하면 로그인 상태를 강제해야 하고, 비회원 사용자한테는 아예 제공이 안 된다. DB 콜도 늘어난다. 반면 localStorage 는 클라이언트 로컬에 바로 쓰고 읽으니까 서버 부하 없이, 비회원도 커버 가능하다. 이번 작업에서 localStorage 를 선택한 건 그 트레이드오프를 따져본 결과였다.
| 저장 방식 | 로그인 필요 | 서버 부하 | 기기 간 동기화 | 구현 복잡도 |
|---|---|---|---|---|
| 서버 DB | O | 있음 | 가능 | 높음 |
| 세션 (서버) | O | 있음 | 불가 | 중간 |
sessionStorage |
X | 없음 | 불가 | 낮음 (탭 닫으면 소멸) |
localStorage |
X | 없음 | 불가 | 낮음 (영속) |
비회원 포함 전체 커버, 서버 부하 없음, 구현 빠름. 현재 단계에선 localStorage 가 가장 합리적인 선택이었다.
실제 작업 내용
변경 파일은 productDetail.jsp 하나다. 상품 상세 페이지에 진입하는 순간, 해당 상품 정보를 localStorage 에 push 하는 스크립트를 심었다.
기본 패턴은 이렇다:
(function () {
var KEY = 'recentProducts';
var MAX = 10; // 최대 보관 개수
var product = {
id: '${product.productId}',
name: '${product.productName}',
image: '${product.imageUrl}',
price: '${product.price}'
};
var list = JSON.parse(localStorage.getItem(KEY) || '[]');
// 중복 제거: 같은 상품이 이미 있으면 앞으로 꺼낸다
list = list.filter(function (p) { return p.id !== product.id; });
list.unshift(product);
// 최대 개수 초과 시 오래된 것 제거
if (list.length > MAX) { list = list.slice(0, MAX); }
localStorage.setItem(KEY, JSON.stringify(list));
})();
JSP 에서 서버사이드로 렌더링된 상품 정보를 JS 변수로 받아서, 그걸 바로 localStorage 에 밀어넣는 구조다. 즉시 실행 함수(IIFE)로 감싸서 전역 스코프 오염을 막았다.
신경 쓴 포인트는 두 가지였다.
- 중복 제거: 같은 상품 상세를 여러 번 들어가면 목록에 중복이 쌓이면 안 된다. 먼저 기존 목록에서 같은 id 를 filter 로 제거하고, unshift 로 맨 앞에 추가하는 방식으로 "가장 최근 본 것이 앞"을 유지했다.
- 최대 개수 제한:
localStorage는 도메인당 보통 5MB 제한이 있다. 이미지 URL 이나 긴 문자열이 계속 쌓이면 용량을 예상보다 많이 차지할 수 있다. 보관 개수 상한(MAX)을 두고 오래된 항목을 slice 로 잘라냈다.
변경이 단일 파일인 이유
productDetail.jsp 하나만 건드렸다. 처음엔 공통 JS 파일로 빼는 것도 고민했는데, 지금 단계에선 상품 상세 진입 시 동작이 목적이니까 해당 페이지에만 두는 게 맞다고 판단했다. 공통 파일로 빼면 "어느 페이지에서 push 되는지" 가 불명확해지고, 나중에 다른 페이지에서도 쓰게 될 때 조건 분기가 복잡해질 가능성이 있다.
단일 파일 변경이라 diff 도 작고, 코드리뷰 부담도 낮다. 핀포인트 수정의 장점이다.
이런 작업에서 흔히 놓치는 것들
비슷한 기능을 구현할 때 보면 빠뜨리는 케이스가 몇 가지 있다.
localStorage를 아예 막아둔 브라우저 환경(사생활 보호 모드 일부) 에서setItem이 예외를 던질 수 있다. try-catch 로 감싸두는 게 안전하다.- 상품 데이터 구조가 바뀌면 기존에 저장된 JSON 이 파싱에 실패하거나 undefined 필드가 생긴다. 스키마 버전 필드를 넣어두거나, 읽을 때 방어 코드를 두는 게 좋다.
- 저장하는 필드가 많아질수록 용량이 빠르게 찬다. 이미지 URL 이나 설명 텍스트가 길면 특히. 필요한 최소 필드만 저장하는 것을 원칙으로 했다.
이 세 가지는 나중에 팀원들한테도 공유해둘 포인트다. 기능이 단순해 보여도 엣지케이스가 은근히 많다.
다음은 저장된 데이터를 읽어서 화면에 렌더링하는 쪽 작업이다.
댓글 0
첫 댓글 달아줘.