상세 컨텐츠

본문 제목

이화마켓 웹어플리케이션 개발 : 리뷰/판매내역 화면 구현하기

코딩 기록

by jii 2023. 12. 2. 20:43

본문

이화마켓 웹어플리케이션 개발 : 리뷰/판매내역 화면 해설

현재 진행 중인 이화 마켓 웹어플리케이션 개발 과정에서 다양한 화면 및 기능들을 구상하고, 구현하고 있다. 우리만의 특색있는 기능을 구현하기 위해 기본적으로 마켓이 갖추어야 할 기능외에도 경매 기능을 추가하였다. 

 

내가 맡은 부분은 리뷰/ 판매 내역 화면이다. 보통의 마켓 사이트들에서는 리뷰가 한 상품에 대한 리뷰일 것이다. 그러나 우리 마켓의 경우 사용자가 중고 물품을 팔거나 경매에 내놓는 경우가 대다수이기 때문에 사용자 한명에 대한 리뷰를 모아볼 수 있는 것으로 설정했다. 


유저 리뷰 화면

다른 사용자들이 특정 사용자 A에 대해 리뷰를 등록하면 그 리뷰들을 모아볼 수 있는 화면이다.

아래 코드는 리뷰 등록 시 데이터 베이스에 등록되는 코드이다. 리뷰 제목, 내용, 그리고 별점 등의 내용들을 입력받고 db에 저장된다.

-database.py

 # 리뷰 데이터베이스에 등록하기
    def reg_review(self, data, img_path, buyerId, sellerId):
        review_info = {
            "sellerId": sellerId,
            "buyerId": buyerId,
            "reviewTitle": data["reviewTitle"],
            "reviewContents": data["reviewContents"],
            "starsVariable": data["starsVariable"],
            "img_path": img_path
        }
        review_data = self.db.child("users").child(
            sellerId).child("user_reviews").push(review_info)
        review_key = review_data['name']
        return review_key

 

여기서 데이터 베이스에 등록된 정보들 중, 특정 아이디의 리뷰들을 불러오는 것이 유저리뷰 화면의 기능이다. 따라서 sellerId의 리뷰를 불러오는 코드는 다음과 같다.

-database.py

 # 특정 아이디의 리뷰들을 데이터베이스에서 불러오기
    def get_reviews(self, sellerId):
        items = self.db.child("users").child(
            sellerId).child("user_reviews").get().val()
        return items

 

불러온 리뷰들을 특정 형식으로 화면에 띄우고자 한다. 1열 5행의 형태로 불러오기 위해서는 per_page와 per_row를 다음과 같이 설정한다. 또한 get_reviews() 함수를 통해 리뷰들을 읽어온다. 페이지네이션 기능을 구현하기 위한 코드도 포함되어 있다. 

-app.py

@application.route("/user_reviews/<id>/")
def view_user_reviews(id):
    page = request.args.get("page", 0, type=int)
    per_page = 5  # item count to display per page
    per_row = 1  # item count to display per row
    row_count = int(per_page/per_row)
    start_idx = per_page*page
    end_idx = per_page*(page+1)  # 페이지 인덱스로 start_idx, end_idx 생성
    data = DB.get_reviews(id)  # read the table
    if not data:
        data = {}
        item_counts = 0
    else:
        item_counts = len(data)

    # 한 페이지에 start_idx, end_idx 만큼 읽어오기
    data = dict(list(data.items())[start_idx:end_idx])
    tot_count = len(data)
    for i in range(row_count):  # last row
        if (i == row_count-1) and (tot_count % per_row != 0):
            locals()['data_{}'.format(i)] = dict(
                list(data.items())[i*per_row:])
        else:
            locals()['data_{}'.format(i)] = dict(
                list(data.items())[i*per_row:(i+1)*per_row])

    return render_template(
        "user_reviews.html",
        datas=data.items(),
        row1=locals()['data_0'].items(),
        row2=locals()['data_1'].items(),
        row3=locals()['data_2'].items(),
        row4=locals()['data_3'].items(),
        row5=locals()['data_4'].items(),
        limit=per_page,
        page=page,  # 현재 페이지 인덱스
        page_count=int((item_counts/per_page) + 1),  # 페이지 개수
        total=item_counts,
        id=id
    )

 

유저 리뷰 화면에 대한 html 코드는 다음과 같다. 세부적인 디자인은 css 파일에서 정의하면 되고, row2,3,4,5에 대한 코드는 1과 동일해 생략하였다. 페이지네이션 기능에 대한 코드도 포함되어 있다. 리뷰가 없으면 등록된 리뷰가 없다고 표시되고 리뷰가 있으면 개수를 계산해 표시한다. 이 부분에서 중요한 것은, 이미지 파일의 경로 설정과 리뷰 클릭시 리뷰 세부 화면으로 넘어가는 경로 설정이다. 

  • 이미지 파일 경로 설정 : 리뷰 등록시 저장된 이미지 파일의 경로는 "/static/images/reviews/{{value.img_path}}"이다.
  • 리뷰 세부 화면 경로 설정 : 키값으로 그 사용자에 해당하는 리뷰 세부 화면으로 이동한다. 

-user_reviews.html

<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="/static/user_reviews.css">
</head>

<body>
    {% extends "index.html" %}

    {% block section %}
    <div class="wrapper">
        <div class="text">{{id}} 리뷰</div>
        <hr>
       
        <div class="topnav">
            <a href="/user_list/{{id}}/">판매내역</a>
            <a href="/user_reviews/{{id}}/">리뷰</a>
        </div>
        <hr>
        {% if total > 0 %}
        <p class="count_review">
            {{total}}개의 리뷰
        </p>
        <div class="review">
            {% for key, value in row1 %}
            <table>
                <td onclick="location.href='/details_of_review/{{value.sellerId}}/{{key}}/';" style="cursor:pointer;">
                    <div class="review-content">
                        <img src="/static/images/reviews/{{value.img_path}}">
                        <div>
                            <p><b>{{value.buyerId}}</b></p>
                            <p>{{value.reviewTitle}}</p>
                            <div class="rating">
                                {% for i in range(0, 5) %}
                                {% if i <= value.starsVariable|default(0)|int %} <span class="filled-star"></span>
                                    {% else %}
                                    <span class="empty-star"></span>
                                    {% endif %}
                                    {% endfor %}
                            </div>
                        </div>
                    </div>
                </td>
            </table>
            {% endfor %}
        </div>

        <div class="page-wrap">
            <div class="page-nation">
                <ul>
                    <li>
                        {% for i in range(page_count) %}
                        <a href="{{url_for('view_user_reviews', id=id, page=i)}}" color="black">{{i+1}}</a>
                        {% endfor %}
                    </li>
                </ul>
            </div>
        </div>

        {% else %}
        <p>
            등록된 리뷰가 없습니다.
        </p>
        {% endif %}
        <button onclick="location.href='/reg_review/{{id}}/';" style="width: 100px; height: auto;">리뷰등록</button>
    </div>
    {% endblock section %}
</body>

 

화면

유저 판매내역 화면

유저 리뷰 조회 화면과 구현 방법은 비슷하다.

그러나 유저 판매 내역을 받아오는 화면에서과 유저 리뷰 조회 화면은 데이터 베이스 코드가 차이가 있다. get_lists() 함수의 기능은 다음과 같다. 

  • id 매개변수를 받아 해당 사용자의 판매내역을 가져온다.
  • 먼저, 주어진 사용자 id를 이용하여 "users" 노드 아래의 "user_list" 노드에서 사용자의 판매내역을 가져온다.
  • 만약 판매내역이 없으면 빈 딕셔너리 matched_items를 반환한다.
  • 그렇지 않으면, 사용자의 판매내역 키들을 리스트로 만든다.
  • 모든 아이템 정보를 담고 있는 "items" 노드에서 각 아이템에 대해 반복하면서, 사용자의 판매내역에 해당하는 아이템만을 matched_items에 추가한다.
  • 최종적으로 matched_items 딕셔너리를 반환한다.

단순히 sellerId로 리뷰들을 받아오는 get_reviews()와는 다르다.

 

-database.py

 # 판매내역
    def get_lists(self, id):
        user_list = self.db.child("users").child(id).child("user_list").get()
        matched_items = {}
        if not user_list.each():
            return matched_items
        else:
            user_list_keys = [item.key() for item in user_list.each()]

        items = self.db.child("items").get()
        for item in items.each():
            if item.key() in user_list_keys:
                matched_items[item.key()] = item.val()

        return matched_items

    def get_lists_bykey(self, key):
        items = self.db.child("users").child(id).child("user_lists").get()
        target_value = ""
        for res in items.each():
            key_value = res.key()

            if key_value == key:
                target_value = res.val()
        return target_value

 

-app.py

# 유저 판매내역
@application.route("/user_list/<id>/")
def view_user_list(id):
    page = request.args.get("page", 0, type=int)
    per_page = 5  # item count to display per page
    per_row = 1  # item count to display per row
    row_count = int(per_page/per_row)
    start_idx = per_page*page
    end_idx = per_page*(page+1)  # 페이지 인덱스로 start_idx, end_idx 생성
    data = DB.get_lists(id)  # read the table
    if not data:
        data = {}
        item_counts = 0
    else:
        item_counts = len(data)

    # 한 페이지에 start_idx, end_idx 만큼 읽어오기
    data = dict(list(data.items())[start_idx:end_idx])
    tot_count = len(data)
    for i in range(row_count):  # last row
        if (i == row_count-1) and (tot_count % per_row != 0):
            locals()['data_{}'.format(i)] = dict(
                list(data.items())[i*per_row:])
        else:
            locals()['data_{}'.format(i)] = dict(
                list(data.items())[i*per_row:(i+1)*per_row])

    return render_template(
        "user_list.html",
        datas=data.items(),
        row1=locals()['data_0'].items(),
        row2=locals()['data_1'].items(),
        row3=locals()['data_2'].items(),
        row4=locals()['data_3'].items(),
        row5=locals()['data_4'].items(),
        limit=per_page,
        page=page,  # 현재 페이지 인덱스
        page_count=int((item_counts/per_page) + 1),  # 페이지 개수
        total=item_counts,
        id=id
    )


if __name__ == "__main__":
    application.run(host='0.0.0.0', debug=True)

 

-user_list.html

<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="/static/user_list.css">
</head>

<body>
    {% extends "index.html" %}

    {% block section %}
    <div class="wrapper">
        <div class="text">{{id}} 판매 내역</div>
        <hr>
        <div class="topnav">
            <a href="/user_list/{{id}}/">판매내역</a>
            <a href="/user_reviews/{{id}}/">리뷰</a>
        </div>
        <hr>

        {% if total > 0 %}
        <p class="count_list">
            {{total}}개의 판매 내역
        </p>

        <div class="sell">
            {% for key, value in row1 %}
            <table>
                <td onclick="location.href='/details_of_item/{{key}}/';" style="cursor:pointer;">
                    <div class="content">
                        <img src="/static/images/items/{{value.img_path}}">
                        <div>
                            <p class="id_seller"><b>{{value.sellerId}}</b></p>
                            <p><b>{{value.name}}</b></p>
                            <p>{{value.description}}</p>
                       
                        </div>
                    </div>
                </td>
            </table>
            {% endfor %}
        </div>

        <div class="page-wrap">
            <div class="page-nation">
                <ul>
                    <li>
                        {% for i in range(page_count) %}
                        <a href="{{url_for('view_user_list', id=id, page=i)}}" color="black">{{i+1}}</a>
                        {% endfor %}
                    </li>
                </ul>
            </div>
        </div>

        {% else %}
        <p>
            등록된 판매내역이 없습니다.
        </p>
        {% endif %}
    </div>
    {% endblock section %}
</body>

 

 

최종적인 구현 화면은 다음과 같다. 판매내역을 누르면 판매 내역 페이지로, 리뷰를 누르면 리뷰 페이지로 이동한다. 내부 네비바에서 두개의 페이지 중 하나로 이동하면 표시되는 기능을 앞으로 구현할 예정이다. 

화면

 

관련글 더보기

댓글 영역