마더케이몰 리뉴얼 개발 기록 (1) 카페24 스킨 위에 _didy 모듈을 얹은 전체 구조

기본 스킨 위에서 다시 짓기 마더케이몰은 카페24로 운영하는 자사몰이다. 모바일은 카페24 모바일 스킨(mobile9), PC는 별도 스킨(skin36)을 쓴다. 오래 운영하면서 이벤트 페이지와 기획전 템플릿이 계속 늘었고, 메인 화면은 카페24 관리자에서 모듈을 끼워 넣는 식으로만 바꿀 수 있었다. 화면을 하나 고치려고 해도 어느 템플릿이 어디서 불리는지부터 찾아야 했다. 리뉴얼 목표는 크게 두 가지였다. 하나는 모바일 메인을 요즘 유아용품 몰처럼 섹션 단위로 다시 짜는 것이었다. 배너, 카테고리 아이콘, 연령별 추천, 타임세일, 카테고리 랭킹, 리뷰어 추천 필수템, 매거진 같은 블록을 위아래로 쌓고, 상품 카드마다 할인율과 평점을 붙이는 구성이다. 다른 하나는 페이지를 넘길 때마다 화면 전체가 하얗게 깜빡이는 느낌을 줄여서 앱에 가까운 흐름을 만드는 것이었다. 작업은 2026년 2월 19일, 운영 중인 모바일 스킨 파일을 그대로 첫 커밋으로 올리는 것에서 시작했다. 커밋 메시지는 "원본 무손실파일"이었다. 무엇을 바꾸든 이 커밋과 비교하면 원래 동작을 확인할 수 있게 해 두려는 목적이었고, 실제로 리뉴얼 내내 "원복"이라는 커밋을 여러 번 남겼다. 카페24 스킨이라는 제약 카페24 스킨 작업은 일반 웹 프론트엔드와 조건이 많이 다르다. 처음에 정리한 제약은 이랬다. 로컬 실행이 안 된다. 템플릿에는 {$basket_icon} , {$product_no} 같은 예약 변수와 <!--@layout(...)--> , <!--@import(...)--> 지시문이 들어 있다. 이 값들은 카페24 서버가 렌더링할 때만 해석되기 때문에, 결과는 FTP로 올린 뒤 실제 몰에서 봐야 알 수 있다. 서버 코드를 둘 수 없다. 쓸 수 있는 것은 HTML, CSS, JS뿐이다. 상품 목록, 적립금, 관심상품 같은 데이터는 카페24가 렌더링한 페이지나 카페24 프론트 함수( add_wishlist , category_add_basket 등)를 거쳐서만 다룰 수 있다. 운영 중인 몰이다. 장바구니, 주문서, 로그인 화면이 깨지면 바로 매출에 영향이 간다. 결제 흐름에는 PG 화면과 외부 도메인도 섞여 있다. 외부 위젯이 많다. 리뷰 위젯(브이리뷰), 라이브 커머스 SDK, 카카오 버튼 같은 스크립트가 각자 DOM을 건드린다. 내 CSS가 이 위젯을 가리거나, 위젯이 내 레이아웃을 밀어내는 일이 자주 생겼다. 그래서 기본 원칙을 하나 세웠다. 카페24 원본 템플릿은 가능한 한 적게 건드리고, 새로 만드는 것은 별도 폴더에 모은다. _didy 모듈: 원본 옆에 붙이는 구조 새 코드는 전부 _didy/ 폴더에 넣었다. 구성은 단순하다. _didy/sections/*.html — 메인의 섹션 하나가 파일 하나다. banner, category, recommend, timesale, category-ranking, best, foryou, review-picks, magazine, sale-picks 등으로 나눴다. _didy/js/common.js — 레이어 스택과 뒤로가기 연동, 페이지 전환 레이어 같은 공통 동작. _didy/js/didy.js — 섹션별 초기화. 상품을 불러오고, 슬라이더를 붙이고, 할인율을 계산한다. _didy/css/common.css , didy.css — 공통 스타일과 섹션 스타일. _didy/pages/ — 전체 카테고리처럼 레이어 안에서 따로 여는 독립 페이지. 메인 index.html 은 섹션을 순서대로 불러오는 목차 역할만 한다. 카페24의 @import 지시문을 그대로 썼기 때문에 별도 빌드 도구가 필요 없었다. <!--@layout(/layout/basic/main2.html)--> <div class="didy-wrap"> <!-- 1. 메인 배너 --> <!--@import(/_didy/sections/banner.html)--> <!-- 2. 카테고리 아이콘 --> <!--@import(/_didy/sections/category.html)--> <!--@import(/_didy/sections/recommend.html)--> <!--@import(/_didy/sections/timesale.html)--> <!--@import(/_didy/sections/category-ranking.html)--> <!--@import(/_didy/sections/best.html)--> <!-- @import(/_didy/sections/ranking.html) --> </div> <!--@js(/_didy/js/common.js)--> <!--@js(/_didy/js/didy.js)--> 섹션 순서를 바꾸거나 잠시 내리는 일은 이 파일에서 한 줄을 옮기거나 주석으로 막으면 끝난다. 마지막 줄처럼 @import 앞에 공백을 넣어 꺼 둔 섹션이 몇 개 있는데, 운영 쪽 요청으로 섹션을 넣었다 뺐다 할 때 이 방식이 가장 빨랐다. 데이터는 카페24 페이지를 읽어서 섹션에 필요한 상품 정보는 대부분 카페24 상품 목록 페이지를 fetch 로 받아 DOM으로 파싱해서 얻었다. 목록 템플릿에서 상품마다 붙는 li[id^="anchorBoxId_"] 를 찾아 상품번호, 이미지, 브랜드, 상품명, 가격을 뽑는 방식이다. 같은 카테고리를 여러 섹션이 동시에 요청하는 경우가 있어서, 결과 캐시와 진행 중인 요청을 따로 들고 있게 했다. function fetchProducts(cateNo) { var cacheKey = 'c_' + cateNo + '_' + MAX_ITEMS; if (cache[cacheKey]) return Promise.resolve(cache[cacheKey]); if (pendingFetch[cacheKey]) return pendingFetch[cacheKey]; var pages = Math.ceil(MAX_ITEMS / PER_PAGE); var baseUrl = '/product/list.html?cate_no=' + cateNo + '&sort_method=6'; var fetches = []; for (var p = 1; p <= pages; p++) { fetches.push(fetchPage(baseUrl + '&page=' + p)); } // 여러 페이지 결과를 합치고 같은 상품번호는 한 번만 남긴다 ... } 카페24 목록 템플릿은 한 페이지에 정해진 개수만 내려주기 때문에, 더 많이 보여줘야 하는 섹션은 페이지를 나눠 동시에 받은 뒤 합쳤다. 파싱할 때 { 가 들어 있는 상품번호는 버린다. 카페24 변수가 치환되지 않은 채 내려오는 빈 템플릿 줄이 섞여 있어서 넣은 방어 코드다. 배너, 리뷰어 추천, 카테고리 랭킹 저장처럼 카페24 관리자만으로는 다룰 수 없는 데이터는 사내 운영 도구에서 설정한 값을 받아 쓰게 했다. 가격이 없는 이벤트성 상품이 최근 본 상품이나 검색 추천에 섞여 나오는 문제도 있었는데, 가격 필드가 비어 있으면 리스트에서 제외하도록 정리했다. 빈 화면 대신 스켈레톤 섹션 대부분이 페이지를 받아 와서 그리는 구조라, 처음 몇백 밀리초 동안은 메인이 텅 비거나 섹션이 하나씩 튀어나왔다. 3월 3일에 스켈레톤 로딩을 붙였다. 스켈레톤 CSS는 index.html 에 인라인으로 넣고, 실제 콘텐츠는 처음에 opacity:0 , height:0 으로 숨긴다. 외부 CSS와 didy.js 가 준비되면 스켈레톤을 감추고 didy-ready 클래스를 붙여 페이드인한다. 같은 날 일부 섹션에서 스켈레톤이 안 걷히는 문제를 고쳤고, 반복 요소와 애니메이션 모양도 조정했다. 메인 슬라이더 배너와 메뉴가 다시 렌더링될 때 깜빡이는 문제(3월 9일), 랭킹 탭을 바꿀 때 Swiper 슬라이드가 맞지 않는 문제도 이 즈음 하나씩 잡았다. PC 스킨과 배포 규칙 PC 스킨은 3월 30일에 원본 파일을 동기화하는 커밋으로 따로 시작했다. 모바일에서 만든 섹션 구성을 PC 메인에 옮기고, 7월에는 마이샵 화면을 PC 비전에 맞추는 작업을 이어서 했다. PC 쪽에서 가장 신경 쓴 것은 배포였다. Git에 커밋해도 카페24 화면은 바뀌지 않는다. 실제 반영은 FTP 업로드로 이뤄진다. 이 두 가지를 같은 것으로 착각하면, 누군가 FTP에서 직접 고친 내용이 다음 배포 때 덮어써진다. 그래서 git-ftp로 Git 변경분만 스킨 폴더( /sde_design/skin36 )에 올리게 하고, 다음 내용을 README에 적어 두었다. 로컬 저장소 루트가 스킨 루트에 1:1로 대응한다는 경로 매핑 .git-ftp-ignore 로 문서( *.md ), 백업 폴더, IDE 설정, .env , 키 파일, FTP 클라이언트 설정을 업로드에서 제외 git ftp init 이나 catchup 은 서버의 배포 기준점을 바꾸므로 운영 이력을 확인한 뒤에만 쓸 것 긴급 수정으로 FTP에서 직접 고쳤다면 같은 내용을 반드시 Git에도 반영할 것 "수정", "최종" 같은 커밋 메시지 대신 운영 변경 단위로 커밋할 것 마지막 항목은 사실 내 모바일 저장소를 보면서 쓴 것이다. 모바일 쪽에는 "장바구니 수량변경 로직 수정"이 하루에 열 번 찍힌 날도 있고, "no message" 커밋도 몇 개 있다. 카페24는 올려 봐야 결과를 알 수 있으니, 고치고 올리고 확인하는 사이클이 그대로 커밋으로 쌓였다. 나중에 원인을 거슬러 올라갈 때 이런 커밋은 거의 도움이 되지 않았다. 정리하며 리뉴얼 뒤로 메인은 섹션 HTML 단위로 나뉘었다. 섹션 하나를 고칠 때 다른 섹션이나 카페24 원본을 열어 볼 필요가 줄었다. 데이터가 오기 전에는 스켈레톤이 자리를 잡아 둔다. PC 스킨은 경로 매핑과 배포 규칙이 README에 있어서, 다른 개발자도 저장소만 받으면 어디에 무엇을 올려야 하는지 알 수 있다. 다시 한다면 두 가지를 먼저 하겠다. 첫째, 개발용 테스트 스킨을 따로 두고 거기서 확인한 뒤 운영 스킨에 반영하는 흐름을 처음부터 만들겠다. 운영 스킨에 바로 올려 보며 고친 탓에 커밋 이력이 시행착오 로그처럼 남았다. 둘째, 상품 목록 파싱 코드를 섹션마다 따로 두지 않고 처음부터 하나로 모으겠다. 지금 didy.js 에는 비슷한 parseHTML 과 장바구니 처리 코드가 섹션별로 여러 벌 있어서, 카페24 템플릿 구조가 바뀌면 여러 곳을 같이 고쳐야 한다. 이 글에서는 전체 구조만 다뤘다. 다음 글에서는 "페이지 이동을 앱처럼" 만들려다 하루에 방식을 세 번 바꾼 이야기를, 그다음 글에서는 장바구니, 관심상품, 쿠폰처럼 카페24 기본 기능 위에 기능을 덧붙이다 생긴 문제들을 정리한다. 기간: 2026.02 ~ 2026.08 기술: 카페24 스킨(모바일 mobile9, PC skin36), Vanilla JS, CSS3, Swiper, git-ftp 관련 프로젝트: 마더케이몰 리뉴얼