no image
[프로젝트] 파이썬 미니 프로젝트 (Last Day)
14일차에서 코드는 바뀐게 없고 PPT를 제작하였습니다. 이제 벌써 마무리가 되어 제출까지 하게 되었네요. 해당 프로젝트는 e-book 판매를 목적으로 제작되었고 내용은 이미지와 함께 설명해보도록 하겠습니다. PPT 내용은 다음과 같습니다. 저희 조에서 먼저 주목한 것은 '휴대 기기 사용량'입니다. 휴대 기기의 사용량이 늘고, 하나의 장비로 여러 업무를 수행할 수 있게 되었습니다. 특히 한국은 휴대폰 보유율이 93%에 달할 정도로 친숙하다고 할 수 있습니다. 그렇다면 같은 책을 보더라도 종이 책 보다는 전자 책을 더욱 친숙하게 느낄 수 있습니다. 이에 대한 좋은 사례로 '재벌집 막내아들' 드라마를 뽑을 수 있었습니다. 해당 드라마가 이슈가되면서 원작 소설에 대한 관심이 쏟아졌고, 매출이 적게는 6배에서 ..
2023.05.11
no image
[프로젝트] 파이썬 미니 프로젝트 Days.14
목차 기능 소스코드 오류 남은 부분 기능 1. My Page에서 구매한 책을 다운로드할 수 있도록 기능을 추가하였습니다. 내용도 추가하려 했으나 코드 구현의 한계 및 오류로 제목만 출력되게 일단은 만들었습니다. 2. 디자인 수정 삐뚤삐뚤했던 부분들을 균일하게 수정하였고, 영어이던 부분은 되도록 한글화 진행하였습니다. 소스코드 1. PDF 다운로드 from flask import Flask, flash, render_template, request, redirect, url_for, session, Response from flask_mysqldb import MySQL from flask_paginate import Pagination import MySQLdb.cursors import re impor..
2023.05.11
no image
[프로젝트] 파이썬 미니 프로젝트 Day.12/13
목차 기능 소스코드 오류 남은 부분 기능 12, 13일 차에는 디자인에 중점을 두고 작업이 진행되었습니다. 이제 정말 완성이 된 느낌입니다. 소스코드 원래 main.css에 몰아서 링크 되어있었으나 이번 디자인 변경 작업을 진행하며 각각 다른 css파일을 만들어 제작되었습니다. 1. book.css main { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 50px; } .book-detail { display: flex; flex-direction: row; justify-content: space-between; align-items: flex-start; margin-bottom..
2023.05.11
no image
[프로젝트] 파이썬 미니 프로젝트 Day.11
목차 기능 소스코드 및 DB 오류 남은 부분 기능 오늘은 장바구니 기능을 구현하였습니다. 테이블과 데이터를 작성하였고 잘 출력되는 것을 확인하였습니다. 다만 아직 직접 입력하면 오류가 나타나기에 수정이 필요합니다. 소스코드 및 DB 1. app.py 2. book.html 3. books.html {% extends "base.html" %} {% block content %} 도서명 저자 출판사 출판일 가격 책정보 이미지 장르 {% for result in results %} {{ result[1] }} {{ result[2] }} {{ result[3] }} {{ result[4] }} {{ result[5] }} {{ result[6] | truncate(100) }} {{ result[8] }} ..
2023.05.11
no image
[프로젝트] 파이썬 미니 프로젝트 Day.10
기능 실행 오늘은 장바구니 기능과 마이 페이지에 구매한 책 목록을 추가하 였습니다. 기능 실행 화면은 다음과 같습니다. 소스 코드 해당 코드는 다음과 같습니다. html은 조금 길어 따로 작성하였습니다. - app.py - sql 테이블 추가 - base.html # base.html Search {% if 'loggedin' in session %} 로그아웃 | 마이 페이지 | 장바구니 {% else %} 로그인 | 마이 페이지 | 장바구니 {% endif %} 전체 소설 판타지 로맨스 액션 공포 사회 {% block content %}{% endblock %} Copyright © Etevers e-book. All Rights Reserved. - cart.html # cart.html {% ext..
2023.05.11
no image
[프로젝트] 파이썬 미니 프로젝트 Day.9
오늘은 ACC+에 참여하느라 프로젝트에 직접적으로 참여하지는 못하고 간단하게 웹 페이지 오류 수정 정도만 도왔습니다. 그런데 컨퍼런스가 끝나니 팀원들이 장바구니 구현을 끝내버렸네요. 사실 프로젝트가 총 18일 정도로 계획되어 있고 이제야 반정도 왔는데 거의 마무리 단계로 들어가고 있습니다. 조금 빠른감도 있는데 따라가려고 더 악착같이 했네요. 제가 오늘 수정한 부분은 크게 없고 위 사진과 같이 ISBN에 가격이 들어가있고, 가격에 장르가 들어가 있었습니다. 떄문에 테이블 넘버를 바꿔주는 작업을 진행했습니다. 위 사진과 같이 위치를 확인하고 테이블 위치에 맞게 번호를 다시 지정해 주었습니다. 또 가격에는 가볍게 '원' 글자가 뒤에 표시되도록 변경했습니다. 변경 후 잘 출력되는 것을 볼 수 있습니다. 이제 ..
2023.05.11
반응형

14일차에서 코드는 바뀐게 없고 PPT를 제작하였습니다. 이제 벌써 마무리가 되어 제출까지 하게 되었네요. 해당 프로젝트는 e-book 판매를 목적으로 제작되었고 내용은 이미지와 함께 설명해보도록 하겠습니다. PPT 내용은 다음과 같습니다.


저희 조에서 먼저 주목한 것은 '휴대 기기 사용량'입니다. 휴대 기기의 사용량이 늘고, 하나의 장비로 여러 업무를 수행할 수 있게 되었습니다. 특히 한국은 휴대폰 보유율이 93%에 달할 정도로 친숙하다고 할 수 있습니다. 그렇다면 같은 책을 보더라도 종이 책 보다는 전자 책을 더욱 친숙하게 느낄 수 있습니다.

이에 대한 좋은 사례로 '재벌집 막내아들' 드라마를 뽑을 수 있었습니다. 해당 드라마가 이슈가되면서 원작 소설에 대한 관심이 쏟아졌고, 매출이 적게는 6배에서 230배까지 증가했다는 것을 볼 수 있습니다.

2021년도 독서 실태 조사 내용에 따르면 종이책의 비율이 감소하고, 전자책, 오디오 북으로 이동하는 비율이 증가하고 있습니다.

저희가 제공하려 했던 서비스는 다음과 같습니다.

  1. 도서명이나 장르를 선택해서 검색할 수 있게 편의성을 제공합니다.
  2. 도서별로 상세 정보를 제공합니다.
  3. 전자 책을 구매하면 어디서든 사용할 수 있도록 PDF파일을 제공합니다.

어플리케이션의 구성은 다음과 같이 제작 되었습니다.

디지털 시스템으로 변화하면서 다양한 비용을 감소할 수 있습니다. 위 사진에서 소개한 것 말고도, 고객은 배송이 오기까지의 시간을 아껴 기회 비용을 줄일 수 있고, 환경 측면에서 이점을 가져올 수 있습니다.

이외에도 언제 어디서든 사용가능하고, 책이 훼손되어 볼 수 없는 상황이 일어나지 않습니다. 또한 어느 책에 상관 없이 DB에 있다면 다운로드하고 볼 수 있습니다.

이 프로젝트의 방향성을 잡으며 계획한 것은 다음과 같습니다.

  1. 컨텐츠 제공자와  협업을 통해 더 많은 컨텐츠를 비교적 저렴한 가격에 서비스할 수 있도록 합니다.
  2. 공간, 시간 등 낭비를 줄이고, 종이와 잉크 등 자원 사용량을 줄일 수 있어 환경적 이점을 가져올 수 있습니다.
  3. 각 지역에만 존재하는 자료들을 수집해 해당 자료로 컨텐츠를 제작할 수 있도록 합니다.
  4. 다양한 선택지를 제공해 고객 개개인에 맞는 컨텐츠를 사용할 수 있도록 돕습니다.


해당 PPT 파일입니다.

ebook-project-v3-8.pptx
1.55MB

 

 


드디어 이 프로젝트가 마무리 되었습니다. 자료를 조사하면서 놀랐던 점은 ebook보다 종이책이 탄소배출량이 적다는 내용이 있어서 지속가능성 부분을 어떻게 처리해야할지 고민했었습니다. 이번 프로젝트에서 실제 업무를 진행하며 어떻게 PT를 진행해야 하는지 배울 수 있었던 시간이었습니다.

 

실질적으로 필요한 기술을 습득하고 활용하는 것도 필요하지만 문서 작성 능력에 있어서 또한 발전시켜야할 필요가 있다는 것을 깨닫게 되었습니다.

반응형
반응형

목차

  1. 기능
  2. 소스코드
  3. 오류
  4. 남은 부분

기능

1. My Page에서 구매한 책을 다운로드할 수 있도록 기능을 추가하였습니다.

내용도 추가하려 했으나 코드 구현의 한계 및 오류로 제목만 출력되게 일단은 만들었습니다.

 

2. 디자인 수정

삐뚤삐뚤했던 부분들을 균일하게 수정하였고, 영어이던 부분은 되도록 한글화 진행하였습니다.


소스코드

1. PDF 다운로드

from flask import Flask, flash, render_template, request, redirect, url_for, session, Response
from flask_mysqldb import MySQL
from flask_paginate import Pagination
import MySQLdb.cursors
import re
import random
from datetime import datetime
from statistics import mean
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
from io import BytesIO
from urllib.parse import quote
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from os.path import join, dirname

@app.route('/download_pdf')
def download_pdf():
    books = request.args.getlist('book')
   
    buffer = BytesIO()
    p = canvas.Canvas(buffer, pagesize=letter)

    font_path = join(dirname(__file__), 'static', 'fonts', 'NanumGothic.ttf')
    pdfmetrics.registerFont(TTFont('NanumGothic', font_path))
   
    # PDF 안 제목
    p.setFont("NanumGothic", 16)
    p.drawString(2.75 * inch, 10.5 * inch, books[0])
       
    # PDF 파일 저장 및 파일 이름 설정
    p.showPage()
    p.save()
    buffer.seek(0)
    filename = f"{books[0]}.pdf"
    quoted_filename = quote(filename)  # 파일 이름 URL 인코딩
    return Response(buffer, mimetype='application/pdf', headers={
        'Content-Disposition': f'attachment; filename={quoted_filename}; filename*=UTF-8\'\'{quoted_filename}'  # Content-Disposition Header에 filename* 속성 추가
    })

 

2. 회원 정보 수정 CSS 추가

/* center the content in the main div */
main {
    display: flex;
    justify-content: center;
  }
.content {
  display: flex;
  justify-content: center;
  background-color: #ffffff;
  padding: 10px;
  font-size: 23px;
}
 /* style for the contentbar */
    .contentbar input[type="password"],
    .contentbar input[type="text"],
    .contentbar input[type="email"],
    .contentbar input[type="date"],
    .contentbar input[type="tel"] {
  width: 50%;
}
.contentbar {
  justify-content: center;
  align-items: center;
  background-color: #ffffff;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  width: 600px;
  margin-top: 50px;
  margin-bottom: 50px;
}
 
  /* style for the form inputs */
  label {
    display: block;
    margin-bottom: 5px;
    font-weight: bold;
  }
 
  input[type="text"],
  input[type="password"],
  input[type="email"],
  select,
  input[type="date"],
  input[type="tel"] {
    padding: 5px;
    margin-bottom: 10px;
    width: 100%;
    border: 1px solid #ccc;
    border-radius: 3px;
    box-sizing: border-box;
  }
  select {
    width: 50%;
  }
  /* style for the submit button */
  input[type="submit"] {
    background-color: rgb(55, 0, 255);
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 16px;
  }
 
  input[type="submit"]:hover {
    background-color: rgb(55, 0, 255);
  } 

오류

1. PDF 다운로드 시 폰트가 없어서 내려받기 실패 오류 발생

  > 해당 프로젝트 폴더에 폰트 폴더 생성 및 나눔 고딕 폰트를 넣어 놓고 해당 폰트로 내려받기 실행 (해결)

 

2. 이전에 댓글을 남기면 오류가 발생하던 오류

  > MySQL 테이블에 자동 생성부분을 체크 하지않아 값이 생성되지 않았습니다. 때문에 댓글을 추가할 때만 오류가 발생했었고, 현재는 해결되었습니다.

 

이 외에는 CSS 수정이 주를 이뤄서 오류가 크게 없었습니다.


남은 부분

- 발표 PPT 제작

- 기타 소소한 부분 수정

반응형
반응형

목차

  1. 기능
  2. 소스코드
  3. 오류
  4. 남은 부분

기능

12, 13일 차에는 디자인에 중점을 두고 작업이 진행되었습니다. 이제 정말 완성이 된 느낌입니다.


소스코드

원래 main.css에 몰아서 링크 되어있었으나 이번 디자인 변경 작업을 진행하며 각각 다른 css파일을 만들어 제작되었습니다.

1. book.css

main {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 50px;
}
.book-detail {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 20px;
}
.book-info{
    margin-right: 40px
}
.book-info h1 {
    position: relative;
    padding: 0;
    margin-top: 0;
    font-family: "Raleway", sans-serif;
    font-weight: 300;
    font-size: 40px;
    color: #080808;
    -webkit-transition: all 0.4s ease 0s;
    -o-transition: all 0.4s ease 0s;
    transition: all 0.4s ease 0s;
    text-transform: capitalize;
}
.book-info h1:before {
    position: absolute;
    left: 0;
    bottom: -10px;
    width: 100px;
    height: 2px;
    content: "";
    background-color: #c50000;
}
.book-info p {
    margin-top: 20px;
    display: block;
    font-size: 0.5em;
    line-height: 1.3;
    font-size: 16px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 4px;
    line-height: 1.5em;
    padding-left: 0.25em;
    color: rgba(0, 0, 0, 0.8);
    padding-bottom: 10px;
}
.book-detail img{
    width: 300px;
    margin-left: 40px;
    box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.2);
}
/* 장바구니 버튼 */
.book-info form {
    margin-right: auto;
}
.book-info button[type="submit"] {
    padding: 10px 20px;
    font-size: 1.5rem;
    border: none;
    background-color: #000000;
    color: #fff;
    cursor: pointer;
    transition: all 0.2s ease-in-out;
}
.book-info button[type="submit"]:hover {
    background-color: #fff;
    color: #007bff;
}
.book-info button[type="submit"]:focus {
    outline: none;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.5);
}
.book-description{
    max-width: 1020px;
    margin-top: 20px;
    padding: 50px
}
@media screen and (max-width: 768px) {
    main {
        padding: 20px;
    }
    .book-info {
        margin-bottom: 10px;
        align-items: center;
        text-align: center;
    }
    .book-detail img {
        width: 200px;
        margin-bottom: 10px;
    }
    .book-info h1 {
        font-size: 1.8rem;
        margin-bottom: 8px;
    }
    .book-info p {
        font-size: 1.1rem;
        margin-bottom: 3px;
    }
    .book-info form {
        font-size: 1.2rem;
        padding: 8px 16px;
    }
}
/* 평균 평점 부분 */
.book-rating {
    margin-bottom: 20px;
}
.book-rating p {
    font-size: 1.2em;
    margin: 0;
}
/* 리뷰 모음 부분 */
.review-assemble {
    max-width: 1020px;
    display: flex;
    flex-direction: column;
    align-items: center;
    flex-wrap: wrap;
}
.review-assemble h2 {
    font-size: 24px;
    margin-bottom: 10px;
}
.review-assemble ul {
    list-style: none;
    padding: 0;
    margin: 0;
    max-height: 400px;
    overflow-y: auto;
}
.review-assemble li {
    border: 1px solid #ccc;
    padding: 10px;
    margin-bottom: 10px;
    width: 1000px;
  }
.review-assemble p {
    margin: 0;
    margin-bottom: 5px;
}
.review-assemble form {
    display: inline-block;
    margin-left: 10px;
}
.review-assemble button {
    background-color: #fff;
    border: 1px solid #ccc;
    padding: 5px 10px;
    cursor: pointer;
}
.review-assemble button:hover {
    background-color: #f0f0f0;
}
/* 별점 부분 */
.star-rating{
    display: inline-block; /* 하위 별점 이미지들이 있는 영역만 자리를 차지함.*/
    direction: rtl; /* 이모지 순서 반전 */
    border: 0; /* 필드셋 테두리 제거 */
}
.star-rating legend{
    text-align: left;
}
.star-rating input[type=radio]{
    display: none; /* 라디오박스 감춤 */
}
.star-rating label{
    font-size: 3em; /* 이모지 크기 */
    color: transparent; /* 기존 이모지 컬러 제거 */
    text-shadow: 0 0 0 #f0f0f0; /* 새 이모지 색상 부여 */
}
.star-rating label:hover{
    text-shadow: 0 0 0 #a00; /* 마우스 호버 */
}
.star-rating label:hover ~ label{
    text-shadow: 0 0 0 #a00;
}
.star-rating input[type=radio]:checked ~ label{
    text-shadow: 0 0 0 #a00;
}
/* 리뷰 작성 한줄평 부분 */
.review-comment textarea {
    display: block;
    box-sizing: border-box;
    width: 100%;
    height: 100px;
    padding: 10px;
    font-size: 16px;
    line-height: 1.5;
    border: 1px solid #ccc;
    border-radius: 5px;
    resize: none;
    margin-bottom: 20px;
  }
.review-comment label {
    display: inline-block;
    margin-bottom: 5px;
}
 

2. books.css

table {
    border: 1px #a39485 solid;
    font-size: .9em;
    box-shadow: 0 2px 5px rgba(0,0,0,.25);
    width: 100%;
    border-collapse: collapse;
    border-radius: 5px;
    overflow: hidden;
    margin-top: 20px;
    margin-bottom: 20px;
}
th {
    text-align: center;
}  
thead {
    font-weight: bold;
    color: #fff;
    background: #010238;
}
td, th {
    padding: 1em .5em;
    vertical-align: middle;
    text-align: center;
}
td {
    border-bottom: 1px solid rgba(0,0,0,.1);
    background: #fff;
}
@media all and (max-width: 768px) {  
    table, thead, tbody, th, td, tr {
        display: block;
    }
    table {
        position: relative;
        padding-bottom: 0;
        border: none;
        box-shadow: 0 0 10px rgba(0,0,0,.2);
    }
    thead {
        float: left;
        white-space: nowrap;
    }
    tbody {
        overflow-x: auto;
        overflow-y: hidden;
        position: relative;
        white-space: nowrap;
    }
    tr {
        display: inline-block;
        vertical-align: top;
    }
    th {
        text-align: center;
        border-bottom: 1px solid #010238;
    }
    td {
        border-bottom: 1px solid #e5e5e5;
    }
}
/* 페이지 넘기기 */
.pagination {
    display: flex;
    justify-content: center;
    list-style: none;
}
.page-item {
    margin: 0 5px;
}
.page-item.disabled span {
    color: #aaa;
}
.page-item.active span {
    background-color: #000000;
    color: #fff;
    border-color: #000000;
}
.page-link {
    display: block;
    padding: 5px 10px;
    color: #000000;
    border: 1px solid #000000;
    border-radius: 5px;
    text-decoration: none;
}
.page-link:hover {
    background-color: #000000;
    color: #fff;
}

3. cart.css

 .flashes {
    background-color: #f8d7da;
    color: #721c24;
    padding: 10px;
    margin-bottom: 10px;
  }
 
  .cart-card {
    border-radius: 5px;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
    padding: 2rem;
    margin-bottom: 2rem;
  }
 
  .cart-heading {
    font-size: 2rem;
    margin-bottom: 2rem;
  }
 
  .cart-table {
    border-collapse: collapse;
    margin-bottom: 2rem;
    width: 100%;
  }
 
.cart-table th,.cart-table td {
    padding: 1rem;
    border: 1px solid #ffffff;
}
 
  .cart-table th {
    text-align: left;
  }
 
  .cart-remove-button {
    background-color: #fff;
    border: none;
    color: #dc3545;
    cursor: pointer;
    transition: background-color 0.2s ease-in-out;
  }
 
  .cart-remove-button:hover {
    background-color: #dc3545;
    color: #fff;
  }
  .cart-checkout-button {
    display: inline-block;
    background-color: #007bff;
    border: none;
    color: #fff;
    padding: 0.5rem 1rem;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    border-radius: 5px;
  }
 
  .cart-checkout-button:hover {
    background-color: #0056b3;
  }
 

4. checkout.css

body {
    font-family: 'Helvetica Neue', sans-serif;
    background-color: #f4f4f4;
  }
 
  h1 {
    font-size: 2rem;
    margin-bottom: 2rem;
    text-align: center;
  }
 
  table {
    margin: 0 auto;
    border-collapse: collapse;
    background-color: #fff;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
    overflow: hidden;
  }
 
  thead th {
    padding: 1rem;
    text-align: center;
    background-color: #007bff;
    color: #fff;
  }
 
  tbody td {
    padding: 1rem;
    text-align: center;
    border-bottom: 1px solid #ddd;
  }
 
  tfoot th {
    padding: 1rem;
    text-align: right;
    background-color: #007bff;
    color: #fff;
  }
 
  tfoot td {
    padding: 1rem;
    text-align: center;
    font-weight: bold;
  }
 
  tfoot td:before {
    content: '$';
  }
 
  th, td {
    width: 50%;
  }
 
  th:last-child, td:last-child {
    width: 30%;
  }
 
  th:first-child, td:first-child {
    width: 20%;
  }
 

5. main.css

.section {
    padding: 1rem;
  }
 
  h2 {
    font-size: 24px;
    margin-bottom: 1rem;
  }
 
  .book-list {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    flex-wrap: wrap;
  }
 
  .book {
    width: 30%;
    margin-bottom: 1rem;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
    border-radius: 5px;
    overflow: hidden;
  }
 
  .book img {
    width: 70%;
    height: 70%;
    object-fit: cover;
    margin: auto;
    display: block;
    max-width: 200px;
    max-height: 300px;
  }
 
  .book h3 {
    font-size: 18px;
    margin: 0;
    padding: 0.5rem;
  }
 
  .book tbody {
    font-size: 18px;
    margin: 0;
    padding: 0.5rem;
    text-overflow: ellipsis; /* display an ellipsis when text overflows */
    white-space: nowrap; /* do not wrap the text */
    overflow: hidden; /* hide any overflow text */
  }
  .book p {
    font-size: 14px;
    margin: 0;
    padding: 0.5rem;
    text-overflow: ellipsis; /* display an ellipsis when text overflows */
    white-space: nowrap; /* do not wrap the text */
    overflow: hidden; /* hide any overflow text */
  }
 
  .book button {
    display: block;
    margin: 0 auto;
    padding: 0.5rem;
    border: none;
    border-radius: 20px;
    background-color: #333;
    color: #fff;
    font-size: 16px;
    cursor: pointer;
  }
 
  .book button:hover {
    background-color: #666;
  }

오류

1. 같은 가격일 경우 마이페이지에 접속이 불가능

2. 구매가 안되는 오류가 발생(datetime 변경하며 오류 발생)

3. 댓글 작성 시 오류 발생


남은 부분

1. 리뷰 샥제 기능

2. 책 PDF로 다운로드 할 수 있도록 기능 추가


 

반응형
반응형

목차

  1. 기능
  2. 소스코드 및 DB
  3. 오류
  4. 남은 부분

기능

오늘은 장바구니 기능을 구현하였습니다. 테이블과 데이터를 작성하였고 잘 출력되는 것을 확인하였습니다. 다만 아직 직접 입력하면 오류가 나타나기에 수정이 필요합니다.

비로그인 시 출력 화면
로그인 시 출력 화면
리뷰 DB 내용 출력 모습
리뷰 작성 기능
정수만 사용가능하게 했습니다.


소스코드 및 DB

1. app.py

2. book.html

3. books.html

{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="{{ url_for('static', filename='books.css')}}">
<div class="results">
    <table>
        <thead>
            <tr>
                <th>도서명</th>
                <th>저자</th>
                <th>출판사</th>
                <th>출판일</th>
                <th>가격</th>
                <th>책정보</th>
                <th>이미지</th>
                <th>장르</th>
            </tr>
        </thead>
        <tbody>
            {% for result in results %}
            <tr data-href="{{ url_for('book_detail', number=result[0]) }}">
                <td>{{ result[1] }}</td>
                <td>{{ result[2] }}</td>
                <td>{{ result[3] }}</td>
                <td>{{ result[4] }}</td>
                <td>{{ result[5] }}</td>
                <td>{{ result[6] | truncate(100) }}</td>
                <td><img src="{{ result[7] }}"></td>
                <td>{{ result[8] }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
<style>
    tr {
        cursor: pointer;
    }
</style>
<script>
    document.addEventListener("DOMContentLoaded", function() {
        let rows = document.querySelectorAll("tbody tr");
        rows.forEach(row => {
            row.addEventListener("click", function() {
                window.location.href = row.dataset.href;
            });
        });
    });
</script>
<ul class="pagination">
    {% for page_num in pagination.pages %}
        {% if page_num %}
            {% if page_num == pagination.page %}
                <li class="page-item active"><span class="page-link">{{ page_num }}</span></li>
            {% else %}
                <li class="page-item"><a class="page-link" href="{{ url_for('book_by_genre', genre=genre, page=page_num) }}">{{ page_num }}</a></li>
            {% endif %}
        {% else %}
            <li class="page-item disabled"><span class="page-link">…</span></li>
        {% endif %}
    {% endfor %}
</ul>
<h1>리뷰 목록</h1>
<ul>
    {% for review in reviews %}
    <li>
        <h2>{{ review[1] }}</h2>
        <p>{{ review[4] }}</p>
        <p>평점: {{ review[3] }}</p>
        <p>작성일자: {{ review[5] }}</p>
    </li>
    {% endfor %}
</ul>
{% endblock %}

4. 리뷰 DB 테이블 구성


발생 오류

1. 점수 입력 오류

평점과 댓글 입력시 다음과 같은 오류가 출력됩니다. 아직 해결은 되지 않아 수정이 필요합니다.

2. 구매 후 PDF 다운로드 기능 설정 오류

다음 이미지와 같이 구매 후 PDF로 다운로드 할 수 있는 기능을 구현하려 하고 있으나 attachment_filename에서 오류가 많이 발생하고 있고, 아직 원인을 찾지 못하였습니다. PDF 다운로드 버튼까지는 완성했고, URL로도 잘 접속되지만 다운로드가 되지 않는 이유는 다음과 같이 유추할 수 있었습니다.

  • URL이 다운로드 기능을 하지 못하는 것 입니다.
    해당 URL이 각 페이지마다 제작된 것이 아닌 하나의 명령에서 해결하려 하다보니 DB에서 내용을 자체적으로 만들 수 있도록 기능을 구현하려 했으나 빠진 부분이 있는 듯합니다.


남은 부분

- 책 pdf 다운로드 어떻게할지 앞으로 논의 필요함
- 리뷰 테이블 구성
- 리뷰 평점을 사용하여 책 리스트 순서 구현
- 책 상세페이지에 리뷰 추가

- 디자인, 리뷰 삭제 기능 추가해야함

반응형
반응형
기능 실행

오늘은 장바구니 기능과 마이 페이지에 구매한 책 목록을 추가하 였습니다. 기능 실행 화면은 다음과 같습니다.

구매 후 최근한 책에 대해 나와 있습니다.


소스 코드

해당 코드는 다음과 같습니다. html은 조금 길어 따로 작성하였습니다.


- app.py

- sql 테이블 추가

-  base.html

# base.html
<!DOCTYPE html>
<html>
<head>
    <title>My Ebook Page</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='base.css')}}">
</head>
<body>
    <header>
        <a href="{{ url_for('home') }}">
            <img src="{{ url_for('static', filename='logo.png') }}" alt="My Ebook Logo">
        </a>
        <div class="search">
            <form action="{{ url_for('search') }}" method="get">
                <div class="search-container">
                    <input type="text" name="keyword" placeholder="Search...">
                    <button type="submit">Search</button>
                </div>
            </form>
        </div>
        <div class="user">
            {% if 'loggedin' in session %}
            <a href="/logout">로그아웃</a> |
            <a href="/mypage">마이 페이지</a> |
            <a href="/cart">장바구니</a>
            {% else %}
            <a href="/login?next=/mypage">로그인</a> |
            <a href="/mypage">마이 페이지</a> |
            <a href="/cart">장바구니</a>
            {% endif %}
        </div>
    </header>
    <nav>
        <ul>
            <li><a href="{{ url_for('book_by_genre', genre='전체') }}">전체</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='소설') }}">소설</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='판타지') }}">판타지</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='로맨스') }}">로맨스</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='액션') }}">액션</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='공포') }}">공포</a></li>
            <li><a href="{{ url_for('book_by_genre', genre='사회') }}">사회</a></li>
        </ul>
    </nav>
    {% block content %}{% endblock %}
    <footer>
        <p>Copyright &copy;
            <script>
                document.write(new Date().getFullYear())
            </script> Etevers e-book. All Rights Reserved.
        </p>
    </footer>
</body>
</html>

- cart.html

# cart.html
{% extends "base.html" %}
{% block content %}
{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul class="flashes">
      {% for message in messages %}
        <li>{{ message }}</li>
      {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

<h1>Shopping Cart</h1>
{% if cart_items %}
<table>
    <thead>
        <tr>
            <th>책 제목</th>
            <th>가격</th>
        </tr>
    </thead>
    <tbody>
        {% for item in cart_items %}
        <tr>
            <td>{{ item['title'] }}</td>
            <td>{{ item['item_price'] }}</td>
            <td>
                <form action="{{ url_for('remove_from_cart', book_number=item['number']) }}" method="POST">
                    <button type="submit">Remove</button>
                </form>
            </td>
        </tr>
        {% endfor %}
    </tbody>
    <tfoot>
        <tr>
            <td colspan="3">Total Price:</td>
            <td>{{ total_price }}</td>
        </tr>
    </tfoot>
</table>
<a href="{{ url_for('checkout') }}">Checkout</a>
{% else %}
<p>Your cart is empty.</p>
{% endif %}
{% endblock %}

- checkout.html

# checkout.html
{% extends 'base.html' %}
{% block content %}
    <h1>Checkout</h1>
    <p>Thank you for your purchase!</p>
    <table>
        <thead>
            <tr>
                <th>Item</th>
                <th>Price</th>
            </tr>
        </thead>
        <tbody>
            {% for item in cart_items %}
                <tr>
                    <td>{{ item['name'] }}</td>
                    <td>${{ item['price'] }}</td>
                </tr>
            {% endfor %}
        </tbody>
        <tfoot>
            <tr>
                <th>Total:</th>
                <td>${{ total_price }}</td>
            </tr>
        </tfoot>
    </table>
{% endblock %}

발생한 오류

처음에는 책을 여러권 구매할 수 있게 설정 했으나 마이페이지에 책 1권까지는 괜찮으나 2개 이상으로 추가되면 페이지 오류가 발생했습니다. 때문에 계정당 1권의 책만 구매할 수 있도록 수정 했습니다. 해당 내용은 app.py에서 add_cart 부분인 다음 사진과 같습니다.

 

이 코드가 계정당 1권만 구매할 수 있도록 하는 원리는 해당 경로에서 코드는 먼저 주어진 번호의 책이 존재하는지 확인하게 됩니다. 책이 존재하는 경우 'purchase' 테이블을 검색하여 현재 계정(session['account'])이 이 책을 이미 구매했는지 확인합니다. 레코드가 발견되면 책이 이미 구매되었음을 의미하며 경고 메시지가 표시됩니다. 기록이 없으면 책이 카트에 추가되고 성공 메시지가 표시됩니다.

이렇게 처리한 결과 해당 오류는 더 이상 출력되지 않았습니다. 또한 처음 이 코드를 생성하는 과정에서 "You have..." 경고문이 여러개 출력되는 버그가 발생했는데, 이는 단순히 로그가 쌓여 그렇게 출력된 것으로 보이며 새롭게 들어온 경우 정상적으로 하나만 출력되는 것을 확인했습니다.

 


프로젝트 남은 부분

- 책 구매 시 pdf 다운로드 기능 구현
- 영어로 되어 있는 부분 한글로 수정
- 리뷰 테이블 구성
- 책 상세페이지에 리뷰 기능 추가
- 리뷰 평점을 사용하여 책 리스트 순서 구현


10일 째가 되니 뭔가 구성이 그럴싸해진 것 같아 점점 더 만족스럽습니다. 처음에는 따라가기도 벅찼고 이해도 힘들었는데 조금씩 읽히고 수정도 직접하고 디자인까지 손보고 있으니 점점 재밌어지는 것 같습니다. 물론 잘될때만 재밌습니다. 살면서 이렇게 스트레스로 편두통이 오는게 얼마만인지 모르겠습니다.

 

화면만 보면 머리아프고 그랬는데, 그래도 이제는 '음~ 이게 문제가 될 수 있겠다.'하고 쉬운 부분을 손 볼 수 있게 되었습니다. 물론 간단한 부분들만 가능하지만요. 동기들도 잘한다, 잘한다 해주니 아주 기분이 날아오를 것 같습니다 ㅎㅎㅎㅎ 하지만 아직 갈길이 멀었으니 열심히 달려보겠습니다!

반응형
반응형

오늘은 ACC+에 참여하느라 프로젝트에 직접적으로 참여하지는 못하고 간단하게 웹 페이지 오류 수정 정도만 도왔습니다. 그런데 컨퍼런스가 끝나니 팀원들이 장바구니 구현을 끝내버렸네요. 사실 프로젝트가 총 18일 정도로 계획되어 있고 이제야 반정도 왔는데 거의 마무리 단계로 들어가고 있습니다. 조금 빠른감도 있는데 따라가려고 더 악착같이 했네요.

 

제가 오늘 수정한 부분은 크게 없고 위 사진과 같이 ISBN에 가격이 들어가있고, 가격에 장르가 들어가 있었습니다. 떄문에 테이블 넘버를 바꿔주는 작업을 진행했습니다.

위 사진과 같이 위치를 확인하고 테이블 위치에 맞게 번호를 다시 지정해 주었습니다. 또 가격에는 가볍게 '원' 글자가 뒤에 표시되도록 변경했습니다.

변경 후 잘 출력되는 것을 볼 수 있습니다. 이제 보니까 장르에 내용이 적혀있네요. 코드에 다음 부분을 추가해주었습니다.

이제 그래도 위치는 제대로 들어가게 되었습니다. 팀원들이 벌써 카트 기능을 구현했는데 오늘 포스팅을 마치고 어떻게 했는지 코드를 분석해봐야겠습니다. 클라우드 엔지니어를 위해 왔는데 점점 프론트 개발자가 되어가는 느낌이네요. ㅎㅎ

반응형