8. 판매자 프로필 만들기

2021. 1. 21. 15:44프로젝트/Salle(살래) 중고거래 웹

728x90

Ⅰ. 판매자 프로필 스펙 & 이슈


사용자의 입장에서 중고거래를 할 때 판매자의 지난 상품 리스트로 레퍼런스 체크를 하는데요, 뿐만 아니라 판매자도 본인의 등록한 상품들을 수정하거나 삭제할 수 있는 하나의 관리화면이 필요합니다.

 

프로필 페이지를 만들기 위해 필요한 정보는 판매 상품 목록(productList), 판매 상품 개수(productCount), 판매 완료 개수 정도(soldProductCount)이 있으며 추가할 기능으로는 판매자 본인에 한해 판매글 수정과 삭제 권한 부여가 있습니다. 서버에서는 DB의 PRODUCT 테이블을 그대로 불러오는 SELECT, 수정할 경우 UPDATE, 삭제는 DELETE들(SQL DML(Data Manipulation Language) 들을 사용해주기만 하면 됩니다. 

 

주요 이슈로는 판매글을 수정할 때 기존 이미지 변경불가가 있었습니다. 오류는 아니고 구현방법을 찾지 못해 발생했습니다. 결과적으로 해당 이슈를 제외한 스펙들은 구현을 마칠 수 있었습니다. 코드들은 Ⅱ. MVC 설계 & 코드 에 정리되어 있습니다.

 

Ⅱ. MVC 설계 & 코드


 1. 와이퍼 프레임(UI설계)

툴은 draw io를 사용했습니다.(link) 크게 세 부분으로 나누어집니다. Nav bar, 사용자 정보, 상품 리스트.


 2. View - JSP(JS), CSS

구현된 페이지 입니다.

Salle_profile

사용자 정보는 Controller에서 parameter Model에 각각 nickName과 totalProduct, totalSold를 입력해줬습니다. 아직 판매완료 개수 코드(totalSold)는 구현하지 않았고 다음주 미완성 기능들 마무리 작업에 포함시켜 구현할 예정입니다.

삭제 시 alert로 진행여부를 한번 더 확인하도록 JS confirm 메서드를 사용했습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Profile</title>
<link rel="stylesheet" href="/resources/css/profile.css">
</head>
<body>
 
<%@include file="home.jsp" %>
 
<div class="wrap_profile">
    <div class="member_info">
        <div class="nickName">
            <h1>${nickName}</h1>
        </div>
        <div class="productCount">
            <h2>총 판매상품: ${totalProduct} 건</h2>
            <h2>총 판매완료: ${totalSold} 건</h2>
        </div>
    </div>
 
    <c:forEach var="product" items="${sellerProductList}">
        <div class="wrap_productInfo">
            <div class="wrap_img">
                <img src="${product.pr_img_1}" class="pr_img"/>
            </div>
            <div class="wrap_title_price_hours">
                <a href="<c:url value="/productInfo/${product.pr_id}"/>">
                    <span class="pr_title">${product.pr_title}</span><br>
                    <span class="pr_price">${product.pr_price}원</span><br>
                    <span class="hoursFromUpload">${product.hoursFromUpload}시간 전</span>
                </a>
            </div>
            <c:choose>
                <c:when test="${login.email == product.pr_email}">
                <div class="wrap_update">
                    <a href="<c:url value="/product/${product.pr_id}/edit"/>">
                        <button id="update">
                            수정
                        </button>
                    </a>     
                </div>    
                <div class="wrap_delete">
                    <a href="<c:url value="/product/${product.pr_id}/delete"/>">
                        <button id="delete" onclick="deleteAlert()">
                            삭제
                        </button>
                    </a>
                </div>    
                </c:when>
                <c:otherwise>
                </c:otherwise>                
            </c:choose>
        </div>
    </c:forEach>
</div>
 
    <script type="text/javascript">
            function deleteAlert() {
                
                var result = confirm("정말 삭제하시겠습니까?");
                
                if(result) {
                    alert("삭제되었습니다.");    
                } else {
                    
                }
            }
    
    </script>
 
 
</body>
</html>
cs

 


Profile 페이지 CSS입니다. 상품 하나당 container display를 block으로 잡고 회원정보와 상품정보는 flex로 배치하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
 
*{
    box-sizing: border-box;
    font-size: 11px;
}
 
{
    text-decoration: none;
}
 
span {
    padding-left: 15px;
}
 
button {
    border: medium;
    border-radius: 2px;
    padding: 6px 10px;
    background-color: transparent; 
    cursor: pointer;
}
 
/* profile wrap*/
.wrap_profile {
    width: 1000px;
    display: block;
}
 
/* member Info */
.member_info {
    display: flex;
    margin-left: 100px;
}
 
.productCount {
    margin-left: 30px;
}
 
/* product Info */
.wrap_productInfo {
    display: flex;
    flex-direction: row;
    width: 500px;
    max-width: 500px;
    overflow: hidden;
    height: 100px;
    margin-left: 100px;
    position: relative;
}
.wrap_img {
    align-self: center;        
}
 
.wrap_title_price_hours {
    align-self: center;
}
 
 
.pr_img {
    width: 100px;
    height: 70px;
}
 
.pr_title {
    font-size: 13px;
    font-weight: bold; 
    color: black;
    max-width: 180px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
 
.pr_price {
    font-size: 12px;
    font-weight: bold;
    color: rgba(1, 112, 223, 1);
}
 
.wrap_update {
    position: absolute;
    top: 25px;
    right: 100px;
}
 
.wrap_delete {
    position: absolute;    
    top: 55px;
    right: 100px;
}
 
#update {
    background-color: #bbdefb;
}
#delete{
    background-color: #ffebee;
}
 
 
cs

 3. Controller

URI bracket {} 안에 담긴 정보를 변수로 사용할 수 있는 @PathVariable로 nickName을 받아 사용했습니다. @RequestParam(pr_email)을 required = "false"로 한 이유는 Profile 페이지에 들어갈 수 있는 방법이 두 가지인데 판매자가 메인화면에서 회원정보를 통해 들어가려면 Product 썸네일에 저장된 pr_email를 거치지 않기 때문입니다. 그래서 39행 if문으로 처리를 두 갈래로 해주었습니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.example.demo.controller;
 
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.List;
 
import javax.servlet.http.HttpSession;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
 
import com.example.demo.application.ProductService;
import com.example.demo.application.ProfileService;
import com.example.demo.domain.Login;
import com.example.demo.domain.Product;
 
@Controller
public class ProfileController {
    
    @Autowired
    ProfileService profileService;
    
    @Autowired
    ProductService productService;
    
    @RequestMapping(value = "/profile/{nickName}", method = RequestMethod.GET)
    public String profileReadGet(HttpSession session, @PathVariable String nickName, @RequestParam(value="pr_email", required = falseString pr_email,
            Model model) {
        
        model.addAttribute("nickName", nickName);
        
        List<Product> sellerProductList;
        
        if (pr_email == "") {
            sellerProductList = profileService.getSellerProductList(pr_email);
            model.addAttribute("totalProduct", profileService.getTotalProduct(pr_email));
        } else {
            Login login = (Login)session.getAttribute("login");
            sellerProductList = profileService.getSellerProductList(login.getEmail());
            model.addAttribute("totalProduct", profileService.getTotalProduct(login.getEmail()));            
        }
        //hoursfromupload
        Timestamp tsClient = Timestamp.valueOf(LocalDateTime.now());
        for (Product product : sellerProductList) {
            long diffTime = tsClient.getTime() - product.getPr_reg_date().getTime();
            int hours = (int) (diffTime / (1000 * 3600));
            if (hours < 1) {
                hours = 0;
            }
            product.setHoursFromUpload(hours);
        }
        
        model.addAttribute("sellerProductList", sellerProductList);
        
        return "profile";
    }
cs

 4. Mybatis Mapper(SQL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 
<mapper namespace="com.example.demo.mapper.ProfileMapper">
    
    <select id="getSellerProductList" resultType="Product">
        SELECT *
        FROM PRODUCT
        WHERE PR_EMAIL = #{email};
    </select>
    
    <select id="getTotalProduct" resultType="int">
        SELECT COUNT(*)
        FROM PRODUCT
        WHERE PR_EMAIL = #{email};
    </select>
    
</mapper>
cs

 


 5. Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.demo.application;
 
import java.util.List;
 
import javax.transaction.Transactional;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.example.demo.domain.Product;
import com.example.demo.mapper.ProfileMapper;
 
@Transactional
@Service
public class ProfileService implements ProfileMapper {
    
    @Autowired
    ProfileMapper profileMapper;
 
    @Override
    public List<Product> getSellerProductList(String email) {
 
        return profileMapper.getSellerProductList(email);
    }
 
    @Override
    public int getTotalProduct(String email) {
 
        return profileMapper.getTotalProduct(email);
    }
    
 
}
cs

 

Ⅲ. 정리 및 향후계획


1월 말까지 프로젝트를 마무리 하는 게 목표입니다. 채팅 기능을 구현하고 작업을 하면서 처리하지 못한 이슈들을 완성시킨다면 프로젝트가 끝날 것 같습니다. CSS와 UI보단 서버와 사용자 간 통신이나 DB 저장 관리 작업에 비중을 높여 완성시키려고 합니다.

 

다음 기능은 마지막인 판매자-사용자 간 채팅 기능입니다. 시간이 다소 걸릴 것 같은데 완료되면 업로드할 예정입니다.