회사에서 한국판 소일런트라고 할 수있는 밀스(Meals) 홍보차 PyCon APAC 2016 후원 논의를 하다 여러가지 사유에서 결국 드랍하였고, 그냥 개인으로 참가 신청을 하였다.
참가 신청을 하고보니, 프로그램 목록에서 Django 로 쇼핑몰을 만들자 라는 발표를 발견했다. 이건 내가 3년째 하고 있는 일인데..? 마침 github에 발표 내용에 대해 분야별로 정리된 내용을 공유 해주셨기에, 이것을 기본으로 하여 스스로 정리차 내가 구축한 Intake(https://www.shopintake.com) 버젼으로 작성해 보았다.
필요 : 혼자 쇼핑몰을 만들어야 하는 상황이었고, 그래서 Python/Django를 골랐다.
이미지 파일 다루기
- 스토리지 : AWS S3, CDN : AWS CloudFront
- 이미지 리사이징 / 썸네일 : sorl-thumnail 패키지 활용
- S3 Browser 통한 이미지 관리
배송 확인
- 별도의 API를 사용하거나 만들지 않음
- 대부분의 배송사들이 송장번호 기반의 배송 URL을 제공한다는 점에 착안하여 고객 송장번호를 URL에 삽입하여 해당 배송사 배송 확인 페이지로 바로 연결
이메일 발송
- 가입, 회원등급 변경, 문의 답변, 결제완료, 마케팅 등에 사용됨
- Mailchimp와 Mailchimp에서 제공하는 transactional email 서비스인 Mandrill 사용
- 이메일 클라이언트에서 inline CSS(HTML 태그에 style attribute로 스타일 직접 추가하는 형태) 만을 지원하는 이슈가 있음, 하지만 Mailchimp는 해당 기능을 디폴트로 지원함
- Mailchimp Automation 기능을 활용하여 쇼핑몰의 회원/구매기록 DB와 Mailchimp list를 연동, 다양한 상황에서 자동화된 메일링 발송(회원등급 변경, 장바구니에만 담기고 완료되지 않은 주문, 주문 후 N일이 지난 고객 등등)
결제
- Intake(https://www.shopintake.com) 를 최초 구축하던 2년반전에는 아임포트 같은 아름다운 결제 API 서비스가 없었음
- 당시 PG사들은 SDK로 JSP, ASP, PHP 등 기성 쇼핑몰 개발 주력 언어들만 지원하던 절망적인 상황
- 이니시스 이니라이트 PHP SDK를 1:1로 Python으로 포팅하여 결제 환경을 구축함, 제한적으로 크로스브라우징 구현
- 2015년 크롬 NPAPI 지원 중단 이슈로 인하여 이니시스가 기존 결제 플러그인 대신 ‘웹표준 결제 플러그인’을 도입함, 이 역시 이니시스의 난해한 개발 문서 읽으며 1:1로 Python으로 구현
- 평소 자주 사용하던 서비스 중 하나인 토스(Toss)에서 결제 서비스인 토스페이(TossPay)가 출시되어, 빠르게 도입. 엄청나게 간편한 결제 API에 감탄.
- 카카오 페이(KakaoPay) 역시 빠르게 도입, 카카오페이의 개발/운영 주관사는 다음카카오가 아닌 LG CNS. LG CNS에서는 Python 예제 코드를 제공하였으나, Java스러운 Python코드로 역시 해독이 어려움. 1:1로 Python 스타일로 라이브러리 변경
- 아임포트 런칭 초기에 도입을 고려하였으나, 이미 구축한 legacy가 너무 많아 포기. 유지보수 이슈로 추후 도입 고려 중
관리자/통계 기능
- raw-level의 모델 데이터 CRUD 작업 시에만 django admin 사용. django admin에 grappelli를 얹어서 사용
- 주문관리, 회원관리, 제품관리, 매출 통계 등의 기능이 제공되는 별도의 관리자용 WebUI 구축
- 관리자 페이지엔 회원 정보, 주문 정보, 매출 정보 등 민감한 사항이 많은데 간헐적으로 악의적인 로그인 시도가 감지되어, two factor authentication 으로 OTP(One Time Password) 도입. django-otp 패키지 사용 하였고, 구글에서 제공하는 OTP 앱인 google authenticator를 사용하여 관리자 페이지에 접근하는 모든 멤버들이 로그인 시점에 자신의 휴대전화를 통해 OTP를 제공받도록 함
- django aggregation 활용하여 시간별(일간/주간/월간), 제품별(제품 카테고리/개별 SKU), 주문 유형별(PC, 모바일, 외부채널 등) 매출 통계, 회원 통계를 멤버들이 원하는 조건에 따라 조회 할 수 있도록 구현
- 그래프의 경우 Google Chart 라이브러리 활용
CMS
- 운영도 개발자가 할 것이 아니라면 쇼핑몰엔 기본적으로 상품 상세페이지/공지사항/이벤트 등 CMS(Contents Management System)이 필요함(쇼핑몰 운영하는 멤버는 HTML을 이해하지 못한다=.=)
- Bootstrap 기반의 WYSIWYG 에디터인 summernote(django-summernote) 도입
- 깔끔하고 사용하기 좋은 UI를 가지고 있지만, summernote가 생성하는 CSS는 깨끗하지 않고-일부 inline CSS가 쇼핑몰 자체의 CSS와 충돌을 일으키며 작성된 결과물이 표시될때 지저분하게 표시되는 문제 발생
- django model signal 을 이용하여 저장 이후 summernote가 생성한 HTML검사하고 문제의 소지가 있는 inline CSS 들을 제거하는 기능 개발
디자인
- 쇼핑몰 프론트엔드 디자인은 자체 디자인하여 퍼블리싱만 외주로 작업
- 반응형 웹은 실제 사용자에게 큰 의미가 없다고 생각하여 PC와 모바일 별도로 디자인함
장바구니
- 장바구니는 django-carton 패키지를 clone하여 수정 후 사용
- django-carton은 자신이 구현한 Product Model을 장바구니 item으로 추가할수있고, 장바구니 추가/수량변경/삭제 등 기본적인 장바구니 기능이 구현되어 있음. 장바구니 정보는 session-base로 저장
- 정보를 session에 저장하기 때문에 삭제 되면 안되는 장바구니 정보(회원 로그인 후 장바구니에 담은 물건) 가 휘발되는 문제 발생, 결제 완료 이전 장바구니정보를 JSON으로 serialize 하여 db에 저장하는 기능 추가 구현
- 무료 배송, 구매 조건에 따른 사은품 기능 등을 쇼핑몰에서 추가로 요구되는 기능들 추가 구현함
주문 진행
- 우편번호 검색의 경우 최초에는 우체국에서 제공하는 API를 Wrapping하여 사용하였으나, 한글 인코딩과 관련하여 자주 예상치 못한 변화가 발생하고 API 자체의 상태도 오락가락 하여 문제가 많았음
- 다음 주소 API 공개 시점에 모든 주소 관련 API 를 다음 주소 API로 변경하였음
주문 모델
- 카드/실시간계좌이체 의 경우 주문서 작성과 동시에 주문 완료 여부가 결정되므로 주문 완료 시점에 주문, 배송, 결제 정보, 주문 상품 정보가 담긴 모델을 생성
- 가상계좌입금(무통장)/토스페이 등의 경우 주문 완료 시점과 결제 완료 시점이 다른 문제 발생, 주문 완료 시점에 카드/실시간계좌이체와 동일하게 모든 정보를 저장하되 주문 모델에 주문 상태를 추가하여 ‘입금 대기’상태로 저장함. PG사로 부터 입금 통보가 올 경우 결제 상태를 ‘결제 완료’로 변경하고 배송 시작.
비동기 작업/작업 스케쥴링
- 쇼핑몰에 필요한 비동기 작업 : 사용자의 액션(주문완료 등)과 함께 작업이 실행되어야 하나 그 작업이 사용자의 경험에서 blocking을 유발할 우려가 있는 작업들(eg. 외부 API를 이용한 Email/SMS/카카오 알림톡 발송), 실행시간이 긴 작업(eg. 회원 대상 대량 SMS 발송)
- 쇼핑몰에 필요한 작업 스케쥴링 : 주기적으로 수행되어야 할 작업들(eg. 매일 자정에 당일 매출 통계를 전 멤버한테 메일로 발송하기, 물류센터 API로 부터 자동으로 운송장 정보 가져오기, 특정 기간 동안 쇼핑몰 사용기록이 없는 회원 휴면 계정 처리하기, 매월 N일에 회원들 구매 기록 기반으로 회원 등급 평가하기 등)
- Celery : Python 기반 비동기 Worker, Scheduler를 포함하는 패키지
- 위 기능들을 Celery를 이용하여 구축하였으며 작업 queue로는 AWS ElastiCache 사용
배포
- AWS ElasticBeanstalk 를 사용하여 git push 만으로 배포작업이 가능하도록 구성함
- 기본적으로 특정 트래픽에 도달할 경우 자동으로 인스턴스를 추가하여 auto-scaling이 되도록 구성.
- ElasticBeanstalk Config Script를 사용하여 새 인스턴스가 시작될때는 python package 이외(python package는 ElasticBeanstalk 이 requirements.txt 기반으로 자동으로 설치해줌) 에 필요한 Linux package를 설치하도록 구성함
- AWS ElasticBeanstalk로 배포 할 경우 짧은시간(30초 미만) 동안의 서비스 downtime 이 발생함, 쇼핑몰 입장에선 심각한 수준의 downtime은 아님 -> 가급적 낮 시간동안엔 배포 안하지만, 심각한 문제의 경우 그냥 배포 진행
- 올해 새로 출시된 AWS Certificate Manager 통해서 서버/CDN리소스에 대해 HTTPS 적용
운영
- real-time crash reporting 도구인 Sentry 서버를 자체 구축하고, 해당 서버를 통해 live 서버에서 예상치 못한 exception이 발생할경우 해당 내역을 자동으로 메일, 슬랙을 통해 전송 하도록 함.
- 사내 커뮤니케이션 툴로 사용 중인 슬랙에 Bot을 개발하여 연동함, 해당 Bot은 명령어를 통해 실시간으로 매출 통계 조회, 주문조회, 재고 조회 등이 가능하도록 함.
이외에도 자잘한 기능들이 많지만, 큰 맥락에선 이 정도로 정리가 되는 느낌이다. 쇼핑몰을 기반이 전무한 상태에서 직적 구축하다보니 우여곡절이 많았고 내가 이걸 왜 다 직접 만들고있나?(Why reinvent the wheel?)라는 생각도 들었지만, 결과적으로는 잘한 일이라는 생각이든다. 이미 존재하는 쇼핑몰 솔루션을 선택했더라면, 지금의 Intake와 같이 자유도 높은 쇼핑몰 운영은 아마 힘들었을 것이다.
* 글에서 언급한 패키지/서비스들이 참 많은데 아직 링크를 걸지는 못하였다. 차차 글 내용 추가와 함께 업데이트 예정.