Stationery E-Commerce Web Application with Spring Boot
Author : rhjdev@gmail.com
💬암호화된 프로젝트로서 실행을 위해서는 'Run Configurations - VM Arguments'에 -Djasypt.encryptor.password=****
등록이 요구됩니다. 비밀번호가 일치하지 않을 시 BindException과 함께 APPLICATION FAILED TO START.
👨👨👧 회원 : 로그인 | 회원가입 | 이메일 인증 | 소셜 로그인(카카오/구글) | 아이디 찾기 | 비밀번호 찾기 | 임시 비밀번호 발송
🏡 마이페이지 : 주문/배송 조회 | 리뷰 등록,수정,삭제 | 찜하기 목록 조회,삭제 | 적립금 내역 조회 | 최초 주문 전 휴대폰 번호 인증 | 회원정보수정 | 회원탈퇴
💰 상품 : 헤더 전체 검색 | 찜하기 등록,삭제 | 섹션/브랜드/가격범위/태그/필터별 조회, 장바구니, 바로주문
🚧 고객센터 : 1:1문의 등록,수정,삭제 | 글·댓글내용/작성자/첨부파일명/말머리별 게시판 검색 | 작성자/관리자 한정 게시글 조회 | 댓글 및 대댓글 등록,수정,삭제,실시간 알림
📈 관리자페이지 : 기간별 회원수/매출액/누적 판매량 TOP 8 통계 조회,엑셀 다운로드 | 메인슬라이드/이벤트배너 등록,수정 | 회원 권한부여,계정정지,누적신고수관리 | 게시글 삭제,복구,휴지통관리 | 댓글 삭제,복구,휴지통관리 | 상품 등록,수정,판매상태변경,옵션별추가금액관리,재고관리 | 주문/배송 배송상태변경,실시간 알림
핵심기능 #1. 실시간 알림
-
댓글/대댓글/상품출고/배송완료
에 대해 Server to Client로의 단방향 통신이 가능한 Server-Sent Events(SSE) 기반으로 실시간 알림 기능을 제공합니다. - 각 알림 메시지를 클릭해 해당 게시글 또는 주문상세정보페이지로 이동 및 확인이 가능하며,
메시지 삭제 버튼
을 눌러 더 이상 목록에 노출되지 않도록 제외할 수 있습니다. - 회원이
로그아웃한 사이 발생한 이벤트 역시 재로그인 후 알림 목록에서 확인
할 수가 있습니다.
핵심기능 #2. 전체 상품 검색
- 검색 키워드로서 문자, 숫자 모두 취급해
상품명, 브랜드명, 주요태그
는 물론심두께별 검색
이 가능합니다. -
Commons Lang3
통해 파라미터 타입(parameter type)을 동적으로 구분하도록 작성하였습니다.
WHERE A.PROD_AVAIL_YN = 'Y' <!-- 판매중인 상품에 한하여 검색 -->
AND (
<choose>
<!-- parameterType 동적 구분 / ASCII 코드 기반 '.' 포함 여부 확인 / 취급 중인 상품의 심두께는 2.0 이하 -->
<when test="@org.apache.commons.lang3.math.NumberUtils@isCreatable(keyword)
and @org.apache.commons.lang3.StringUtils@contains(keyword, 46) and keyword lt 2">
O.OPT_POINT_SIZE LIKE TO_NUMBER(#{ keyword })
</when>
<otherwise>
A.PROD_NAME LIKE '%' || #{ keyword } || '%'
OR C.BRAND_NAME LIKE '%' || #{ keyword } || '%'
OR A.PRODUCT_TAG LIKE '%' || #{ keyword } || '%'
</otherwise>
</choose>
)
핵심기능 #3. 잉크색상별 검색 필터
- 색상들을
Enum
상수 필드로 정의하고, 각각DB 저장에 쓰일 값(value)/사용자 화면에 보일 이름(label)/스타일 적용 용도의 헥스코드(color)
와 같은 데이터를 명시한 후 생성자 통해 호출 및 활용하였습니다.
public enum ProductInkColor {
BLACK("black", "블랙", "color: #000000;"); //value, label, color
}
<th:block th:each="ink : ${T(com.reminder.geulbeotmall.product.model.dto.ProductInkColor).values()}">
<span class="color-span" th:data-target="${ ink.getLabel() }">
<a href="#" data-bs-toggle="tooltip" data-bs-placement="top" th:title="${ ink.getLabel() }">
<i class="fa-solid fa-square-full" th:style="${ ink.getColor() }"></i>
</a>
</span>
</th:block>
핵심기능 #4. 최근 본 상품 목록/비로그인 장바구니
- 로그인 여부에 상관 없이 접속 이래 현재까지 조회한 상품 목록을
@SessionAttributes
어노테이션 통해 세션상에recentlyViewed
이름으로 계속 기록합니다. 이후 로그인하게 되면 회원은마이페이지 메인에서 해당 목록을 확인
할 수 있습니다. - 비로그인 상태에서 담은 장바구니 상품은 마찬가지로
@SessionAttributes
어노테이션 통해 세션상에geulbeotCart
로서 기록됩니다. 이어서 로그인이 발생할 경우회원의 장바구니 목록으로 연동 및 저장
됩니다.
핵심기능 #5. 이메일 발송
-
JavaMailSender
를 이용해 이메일 인증 및 임시 비밀번호 발송 기능을 구현하였습니다. - 휘발성 데이터인 이메일 인증 토큰의 경우 인메모리(In-Memory) 형태에 TTL(Time to Live) 특성을 지녀 유효기간이 설정된
Redis
기반의 Refresh Token으로 관리합니다. 사용자는 전송된 링크를 눌러 재접속하는 것만으로 이메일 인증을 완료할 수 있습니다.
핵심기능 #6. 소셜 로그인
- 일반 로그인의 경우 회원가입 양식 작성 후 이메일 인증을 거쳐야 하는 반면, 소셜 로그인한 회원은
해당 계정에서 불러온 이름 및 이메일 정보가 연동
돼 입력란을 채우며 나아가 별도의 이메일 인증 없이 곧바로 이용이 가능합니다.
핵심기능 #7. 적립금 혜택
- 일반 로그인/소셜 로그인 구분 없이 모든 신규 회원은
가입과 동시에 2,000원의 적립금
을 적립 받습니다. -
텍스트리뷰 100원/사진리뷰 300원
으로 적립금 혜택이 주어집니다. 따라서 1)작성자는 작성일로부터 7일 경과 후 게시글 삭제가 가능하며, 2)텍스트리뷰 수정 시 파일 추가가 이뤄진다면 차액을 추가로 적립 받습니다. - 회원은
마이페이지
에서 자신의 적립금 적립/사용 상세 내역을 확인할 수 있습니다.
핵심기능 #8. 휴지통 이동
- 게시글/댓글은 삭제 시
휴지통
에 저장돼100일의 복구기한
이 주어지고, 만료일이 도래하면 자동 영구 삭제됩니다. - 임의로 이동되는 경우에 대비하여
삭제자
를 명시하며, 관리자는 기한 내 이를 복구할 수 있는 권한이 있습니다.
OS | Windows 10 |
---|---|
Language | |
IDE | |
Framework | |
Build Tool | |
Database | |
Frontend | |
Library | |
API | |
Server | |
Version Control |
선택기술 | 선택이유 및 근거 |
---|---|
SSE |
로그인 통해 Server와 Client간 연결이 이뤄지거든 Client에게 추가적인 요청을 요구하지 않으면서도 불시에 발생되는 이벤트들을 즉각 송신할 수 있어야 하기에 Server to Client로의 단방향 통신이 가능한 Server-Sent Events(SSE) 가 실시간 알림을 구현하는 데 적합하다고 판단하였음. |
Redis |
휘발성 데이터인 이메일 인증 토큰 을 데이터베이스에 직접 저장 및 호출하는 것은 불필요한 부하를 초래할 수 있으므로 인메모리(In-Memory) 형태에 TTL(Time to Live) 특성을 지녀 유효기간이 설정된 Redis 기반의 Refresh Token으로서 관리. |
Jasypt |
보안 측면을 고려하여 Jasypt 통해 비밀번호 와 같은 property의 암호화를 진행하고, 어플리케이션 실행 시 값을 요청할 수 있도록 VM arguments로 등록함. |
JUnit5 |
주어진 환경에서(given) 특정 코드가 실행됐을 때(when) 어떤 결과로 이어지는지(then) 모듈 단위의 테스트케이스로 구분. 전제적 요구사항을 점검하고, 의도대로 작동하는지 검증하면서 코드의 생산성 및 유지보수성을 높이고자 함. |
Enum |
문구 쇼핑몰 특성에 맞춰 잉크컬러, 바디컬러, 심두께, 태그, 할인율, 상품카테고리, 기본배송메시지, 카드사별 할부혜택내용, 문의게시판 말머리, 실시간 알림 종류 등을 연관된 상수들의 집합인 열거형 Enum 으로 정의. 각 필드마다 데이터를 배정하고 이를 접근자(getter method) 통해 호출하면서 리팩토링 시 변경 범위를 최소화하였음. |
Commons Lang3 |
상품 전체 검색 시 StringUtils, NumberUtils 등 Apache Commons Lang3 클래스에 정의된 메소드 통해 parameter type을 동적으로 구분 및 처리하도록 조건별 쿼리문을 최적화하였음. |
Thymeleaf Layout |
Alert, Modal, Pagination, Comment, Header/Footer, Dashboard 등을 각각 Fragment 로 구분하여 사이트 전역적으로 사용되는 데 있어 코드 중복을 피하고 추가/수정에 용이하도록 의도하였음. |
MessageSource |
쇼핑몰 확장성 및 코드 유지보수성을 고려하여 필요에 따라 다국어 지원할 수 있도록 앞서 Thymeleaf Layout Fragment 로 구분해둔 알러트(alert) 메시지들은 MessageSource 인터페이스의 properties 로 관리되고 있음. |
@SessionAttributes |
어노테이션 통해 세션상에 정보를 저장하고, 여러 화면 또는 연계된 요청 중에 해당 객체를 공유하도록 정의해 다음과 같이 활용하였음.로그인 확인용 정보(loginMember) : 현재 로그인한 회원의 아이디를 가리키며 나아가 관리자인지 혹은 작성자 본인인지를 구분. 최근 본 상품(recentlyViewed) : 회원은 접속 이래 현재까지 조회한 상품 목록을 '마이페이지'에서 확인 가능. 장바구니(geulBeotCart) : 비로그인 상태에서 담은 장바구니 상품이 회원의 장바구니 목록으로 저장. 바로주문 요청 정보(orderItem) : 로그인 전 선택한 상품 및 세부 옵션 정보 그대로 '주문페이지'로 이어지며, 이는 일회성이기에 사용자의 기존 장바구니와는 무관. 소셜 로그인 여부(signInWithSocialAccount) : 소셜 로그인(카카오/구글) 시 이동 경로 구분. |