Laundrygo_Project
👕Laundrygo Project
‘우리집 모바일 세탁소, 런드리고’의 웹사이트 버전
Spring MVC, MyBatis를 기반으로 제작한 세탁 서비스 웹사이트
어플 사용이 어려울 때, 웹사이트로도 이용가능 하도록 하여 사용자의 불편함을 해소하는 프로젝트
👕제작기간 및 참여인원
FrontEnd
2022-08-07 ~ 2022-08-21
BackEnd
2022-09-18 ~ 2022-10-04
참여인원
팀 프로젝트 (총 4명)
👕사용기술
FrontEnd
BackEnd
👕개발환경
Tools
DBMS
Languages
Team Collaboration Tool
👕설계
흐름도
ERD
👕화면구성 및 기능
1. 메인페이지
2. 회원가입
- validation check(유효성 검사)를 통해 중복체크&입력 누락 시 회원가입X
- ajax를 이용하여 중복확인 버튼 클릭시 중복아이디 체크
3. 로그인
- 로그인 완료시 Topbar [로그인/회원가입] → [로그아웃/마이페이지] 변경
4. ID, PW 찾기
- 같은 이름과 전화번호로 등록된 회원의 모든 아이디를 찾아 보여줌
- 입력값이 없거나 정보가 없는 경우 → 일치하는 정보X alert창
5. 월정액&결제
- 월정액 신청하기 Click → 각 월정액 결제 페이지로 이동
- 결제정보에 회원정보 수정버튼 Click → 마이페이지로 이동
- 보유 포인트 우선차감 Check 시 → 포인트 사용 값이 들어오고 총 결제금액 변경
- 한 개 이상의 월정액을 구매하려는 경우 → 월정액 구매X
6. 수거신청
- 월정액이 없으면 수거신청X
- 수거신청 오른쪽 화살표 Click 시 모달창
- Checkbox 체크시 값 동시차감 가능
7. 고객센터
- 탭 메뉴를 이용하여 각각의 메뉴에 맞는 내용으로 구성
- 검색어 입력 시 관련 내용 Select
- 1:1 문의하기 Click 시 로그인이 되어있다면 문의하기 페이지로 이동
8. 문의하기
- 문의 유형 선택X → 문의하기X
- MultipartResolver를 이용하여 파일 크기를 설정
- 문의하기 완료 → 나의 문의내역에서 내가 신청한 문의 내용들만 모아 볼수있음
- 문의 내용 첫줄의 문자 10개까지 제목으로 나옴
9. 마이페이지
포인트
- 사용자의 보유 포인트와 포인트 사용내역 확인
결제내역
- 사용자의 결제내역을 확인
내정보
- 사용자의 정보를 확인
- 비밀번호 Input에 현재 비밀번호 입력 후 비밀번호가 일치 했을 때 수정버튼 Click
→ 새 비밀번호 설정 Input Block- 전화번호, 주소, 카드 변경가능
- 탈퇴버튼 Click 시 alert창으로 재확인
이용중
- 사용자의 이용중인 상품과 사용내역을 확인
- 월정액 매월 결제일을 알려줌
- 해지하기 Click 해지 완료 후 → 월정액 결제일이 해지 예정일로 변경
- 수거신청 시 → 남은 [생활빨래, 개별 클리닝, 무료수거배송] 개수 차감
👕트러블 슈팅
1:1 문의 이미지 등록 시 이미지 저장 및 받아오는 기능
// cs_uuid 만드는 로직
UUID uuid = UUID.randomUUID();
String cs_uuid = uuid + "";
// 문의하기 객체 생성
Cs cs = new Cs(email, cs_type, cs_title, cs_content, cs_uuid);
// 이미지 저장 처리
// img_name은 UUID + 사용자가 저장한 이미지 이름
// img_file은 저장경로
String imgPath = "C:\\uploadImg\\";
Map<String, Object> listMap = new HashMap<>();
for(MultipartFile mf : cs_img){
UUID uuid2 = UUID.randomUUID();
String imgName = uuid2 + "_" + mf.getOriginalFilename();
File saveImg = new File(imgPath, imgName);
mf.transferTo(saveImg);
int extra = imgName.lastIndexOf(".");
String imgName_x = imgName.substring(0, extra);
listMap.put("img_name", imgName_x);
listMap.put("img_file", imgPath + imgName);
listMap.put("cs_uuid", cs_uuid);
System.out.println("map에 들어가는지 ???? " + listMap);
// 서비스 단에 map 넘기기
csService.imgSave(listMap);
}
@GetMapping("/qna")
public String qna( Model model, HttpSession session, RedirectAttributes rattr ) throws Exception {
String email = (String)session.getAttribute("email");
if( email == null ) {
rattr.addFlashAttribute("qna_login", "not");
return "redirect:/cs";
}
List<Cs> css = csService.selectCs(email);
System.out.println("css = " + css);
if( css != null ) {
model.addAttribute("css", css);
}
List<String> cs_uuid = csService.selectUuid(email);
System.out.println("cs_uuid = " + cs_uuid);
List<CSImg> csImg = new ArrayList<>();
for (int i=0; i<cs_uuid.size(); i++){
List<CSImg> csImgList = new ArrayList<>();
String idx = cs_uuid.get(i);
System.out.println("디비에서 받아온 uuid : " + csService.selectCsImg(idx));
csImgList = csService.selectCsImg(idx);
for(int j=0; j<csImgList.size(); j++){
csImg.add(csImgList.get(j));
}
}
if( csImg.size() > 0 ) {
System.out.println("이거야?" + csImg.get(0).getImg_name());
model.addAttribute("csImg", csImg);
}
return "questions";
}
- 이미지를 임의의 컴퓨터 경로에 저장
- 랜덤한 uuid와 이미지 저장 경로를 cs, cs_img 테이블에 저장
- 게시판에서는 각 문의가 고유한 uuid로 저장되고, 해당 문의의 uuid와 일치하는 이미지 경로를 테이블에서 받아와 이미지를 띄운다.
특정 기능 수행 후 페이지 URL 변경으로 인해 타 기능들이 수행되지 않는 문제
@RequestMapping(value="/findId", method = RequestMethod.POST)
public String findId(User user, RedirectAttributes rattr, HttpServletRequest req) throws Exception {
System.out.println("id찾기 controller");
String uri = req.getHeader("REFERER");
String username = req.getParameter("find_email_name");
String userphone = req.getParameter("find_email_tel");
List<User> id = userService.findId(username, userphone);
System.out.println("id = " + id);
StringBuilder idList = new StringBuilder();
if(id.size() == 0){
rattr.addFlashAttribute("check", 1);
} else {
for( int i = 0; i < id.size(); i++ ) {
idList.append(id.get(i).getEmail());
idList.append(", ");
}
int lastEmail = idList.lastIndexOf(",");
idList = new StringBuilder(idList.substring(0, lastEmail));
rattr.addFlashAttribute("check", 2);
rattr.addFlashAttribute("message","아이디는 "+ idList +" 입니다.");
}
return "redirect:"+uri;
}
- id 찾기 기능을 예시로, id 찾기 수행시 메인페이지에서 Request url인 “/findId” 로 url이 변경된다.
- 이때, 기본적인 로그인 및 여러 페이지로의 이동이 불가능하게 됨
- 따라서 메서드의 return문을 “redirect:” + uri 로 설정하여 이전페이지의 url로 기능을 수행하여 값을 받아온다.
ID 찾기 기능에서 한 명이 두 개의 이메일을 보유하고 있는 상황에 대한 처리
@RequestMapping(value="/findId", method = RequestMethod.POST)
public String findId(User user, RedirectAttributes rattr, HttpServletRequest req) throws Exception {
System.out.println("id찾기 controller");
String uri = req.getHeader("REFERER");
String username = req.getParameter("find_email_name");
String userphone = req.getParameter("find_email_tel");
List<User> id = userService.findId(username, userphone);
System.out.println("id = " + id);
StringBuilder idList = new StringBuilder();
if(id.size() == 0){
rattr.addFlashAttribute("check", 1);
} else {
for( int i = 0; i < id.size(); i++ ) {
idList.append(id.get(i).getEmail());
idList.append(", ");
}
int lastEmail = idList.lastIndexOf(",");
idList = new StringBuilder(idList.substring(0, lastEmail));
rattr.addFlashAttribute("check", 2);
rattr.addFlashAttribute("message","아이디는 "+ idList +" 입니다.");
}
return "redirect:"+uri;
}
@Override
public List<User> findId(String name, String phone) throws Exception {
System.out.println("id찾기Dao");
Map map = new HashMap();
map.put("name", name);
map.put("phone",phone);
return session.selectList(namespace + "findId", map);
}
- 동일한 이름, 동일한 전화번호로 회원가입을 두 번 이상 진행하였을 때, UserDao 부분에서 “selectOne” 함수에 오류가 발생하였다.
- 해당 현상을 대처하기 위해 UserDao 부분에서 “selectList” 함수로 수정하였고, List로 받아온 값들의 email 부분만 StringBuilder로 설정하여 기능을 진행하였다.
월정액 기간이 만료되었을 때 처리되는 로직 구현
int life_cnt = 0;
int cleaning_cnt = 0;
int free_cnt = 0;
int m_price = 0;
int extra_charge = 0;
String card_type = null;
String card_num = null;
PayList payList = new PayList();
LocalDateTime start_date = LocalDateTime.now().withNano(0);
LocalDateTime new_end_date = start_date.plusMonths(1);
Monthly monthly = null;
for( int i = 0; i < monthlyPayList.size(); i++ ) {
email = monthlyPayList.get(i).getEmail();
end_date = monthlyPayList.get(i).getEnd_date();
keep = monthlyPayList.get(i).getKeep();
name = monthlyPayList.get(i).getM_name();
m_price = monthlyPayList.get(i).getM_price();
card_type = monthlyPayList.get(i).getCard_type();
card_num = monthlyPayList.get(i).getCard_num();
extra_charge = monthlyPayList.get(i).getExtra_charge();
if(now.isAfter(end_date)) {
if( keep == 1 ) {
monthly = monthlyService.monthlyInfo(name);
life_cnt = monthly.getLife();
cleaning_cnt = monthly.getCleaning();
free_cnt = monthly.getFree();
payList.setEmail(email);
payList.setM_name(name);
payList.setM_price(m_price);
payList.setExtra_charge(extra_charge);
payList.setTotal_price(m_price+extra_charge);
payList.setPay_date(start_date);
payList.setCard_num(card_num);
payList.setCard_type(card_type);
monthlyService.new_monthlyListUpdate(email, life_cnt, cleaning_cnt, free_cnt, start_date, new_end_date);
monthlyService.payListInsert(payList);
} else if( keep == 0) {
if (extra_charge > 0 ) {
payList.setEmail(email);
payList.setM_name(name);
payList.setM_price(0);
payList.setExtra_charge(extra_charge);
payList.setTotal_price(extra_charge);
payList.setPay_date(start_date);
payList.setCard_num(card_num);
payList.setCard_type(card_type);
monthlyService.payListInsert(payList);
}
monthlyService.monthlyListDelete(email);
}
}
- 메인페이지가 로드될 때 일괄적으로 기간이 만료된 월정액을 파악한 후, 처리를 진행하였다.
- 월정액 기간이 종료되었을 때, 사용자가 설정한 월정액의 유지 또는 해지 상황을 기반으로 처리하였다.
- 월정액을 유지하였을 때, 동일한 월정액의 기간을 재설정하고, 추가 금액을 0으로 만든 후 결제내역에 기존 월정액 금액 + 추가금액 만큼을 insert 하였다.
- 마찬가지로 잔여 생활빨래, 개별클리닝, 무료수거의 갯수를 월정액 초기 횟수로 재설정 하였다.
- 월정액을 해지하였을 때, 해당 월정액을 DB에서 삭제하고, 추가금액 만큼을 결제내역에 insert 하였다.
프로젝트 진행과정의 분업과 효율성에 대한 고민
- 네 명의 인원이 각각의 파트를 맡아 개발을 진행하였을 때, 서로의 개발 코드에 대한 이해도가 떨어질 수 있다는 문제점이 있다.
- 반대로 네 명의 인원이 함께 모든 파트를 구현하기에는 시간적으로 효율성이 떨어진다는 판단을 하였다.
- 따라서 프로젝트 파트를 두개로 나눠 함께 구현함으로써 효율성과 원활한 개발코드 공유를 도모하였다.
지속적인 DB의 수정 및 FK값 설정 문제
- 프로젝트를 진행함에 있어 추가되거나 삭제되는 DB가 존재하였다.
- 해당 DB와 관련된 controller 개발을 중심으로 이외의 기능들까지 고려하여 DB 추가 또는 삭제, 수정을 진행하였다.
- FK값에 따라 회원정보 삭제, 월정액 구매내역 삭제 등 기능이 수행되지 않는 현상이 발생하였다.
- 논의를 거쳐 불필요한 FK값을 삭제하거나, CASCADE 및 RESTRICT 옵션을 설정하여 원활한 기능 구현을 성공하였다.
Leave a comment