[내일배움캠프 사전공부 웹개발 4주차]

안떽왕·2023년 2월 23일
0

프로젝트

가상환경 구축하기

  1. 프로젝트를 만들 폴더 선택
  2. app.py 파일 생성
  3. 터미널 켜기
  4. python -m venv venv → 터미널에 입력
  5. 터미널을 종료하고 밑에 3.x.x. 버전 클릭 후 venv가 적힌 문구 선택
  6. 기존 터미널을 없애고 새 터미널 생성 후 venv가 적혀져 있는지 확인

플라스크 프레임워크

venv 터미널에 pip install flask 입력해서 플라스크 프레임워크 다운받기

<app.py>

from flask import Flask
app = Flask(__name__)

@app.route('/')
def home():
   return 'This is Home!'

@app.route('/mypage')
def mypage():
   return '<button>버튼입니다'

if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)

브라우저를 실행시키고 localhost:5000을 입력하면 This is home 이라는 문구가 적힌 사이트로 연결되고
localhost:5000/mypage를 입력하면 버튼입니다 라는 버튼이 생기는 걸 볼 수 있다.

index.html 불러오기

app.py파일에 직접 html코드를 작성해도 정상 작동하지만 가독성이 좋지않아 불러오는 방식으로 처리한다.
1. templates폴더를 만든다
2. index.html 파일을 만든다.
3. 원하는 html정보를 index.html에 작성한다.
4. app.py파일에 render_template를 import 하고, 원하는 주소에 리턴값을 render_template(’index.html’)로 변경한다.

<index.html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <title>Document</title>

    <script>
        function hey(){
            alert('안녕!')
        }
    </script>
</head>
<body>
    <h1>제목을 입력합니다</h1>
    <button onclick="hey()">나는 버튼!</button>
</body>
</html>

<app.py>

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def home():
   return 'This is Home!'

@app.route('/mypage')
def mypage():
   return render_template('index.html')

if __name__ == '__main__':  
   app.run('0.0.0.0',port=5000,debug=True)

GET / POST 방식

GET 요청

  • 통상적으로 데이터 조회(Read)를 요청할때 사용(ex)영화 목록 조회)
  • 데이터 전달: URL 뒤에 물음표를 붙여 key = value로 전달

POST 요청

  • 통상적으로 데이터 생성(Create), 변경(Update), 삭제(Delete) 요청 할 때 사용(ex)회원가입,탈퇴, 비밀번호 수정)
  • 데이터 전달: 바로 보이지 않는 HTML

화성땅 구매 프로젝트

새로운 폴더에서 진행하기에 위에있는 파일들과는 전혀 연관이 없다.

<app.py>

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/mars", methods=["POST"])
def mars_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결 완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'msg':'GET 연결 완료!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

<index.html>

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Batang:wght@400;700&display=swap" rel="stylesheet" />

    <title>선착순 공동구매</title>

    <style>
        * {
            font-family: "Gowun Batang", serif;
            color: white;
        }

        body {
            background-image: linear-gradient(0deg,
                    rgba(0, 0, 0, 0.5),
                    rgba(0, 0, 0, 0.5)),
                url("https://cdn.aitimes.com/news/photo/202010/132592_129694_3139.jpg");
            background-position: center;
            background-size: cover;
        }

        h1 {
            font-weight: bold;
        }

        .order {
            width: 500px;
            margin: 60px auto 0px auto;
            padding-bottom: 60px;
        }

        .mybtn {
            width: 100%;
        }

        .order>table {
            margin: 40px 0;
            font-size: 18px;
        }

        option {
            color: black;
        }
    </style>
    <script>
        $(document).ready(function () {
            show_order();
        });
        function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }
        function save_order() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data);
                alert(data["msg"]);
            });
        }
    </script>
</head>

<body>
    <div class="mask"></div>
    <div class="order">
        <h1>화성에 땅 사놓기!</h1>
        <h3>가격: 평 당 500원</h3>
        <p>
            화성에 땅을 사둘 수 있다고?<br />
            앞으로 백년 간 오지 않을 기회. 화성에서 즐기는 노후!
        </p>
        <div class="order-info">
            <div class="input-group mb-3">
                <span class="input-group-text">이름</span>
                <input id="name" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <span class="input-group-text">주소</span>
                <input id="address" type="text" class="form-control" />
            </div>
            <div class="input-group mb-3">
                <label class="input-group-text" for="size">평수</label>
                <select class="form-select" id="size">
                    <option selected>-- 주문 평수 --</option>
                    <option value="10평">10평</option>
                    <option value="20평">20평</option>
                    <option value="30평">30평</option>
                    <option value="40평">40평</option>
                    <option value="50평">50평</option>
                </select>
            </div>
            <button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
            </button>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th scope="col">이름</th>
                    <th scope="col">주소</th>
                    <th scope="col">평수</th>
                </tr>
            </thead>
            <tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20평</td>
                </tr>
                <tr>
                    <td>임꺽정</td>
                    <td>부산시 동구</td>
                    <td>10평</td>
                </tr>
                <tr>
                    <td>세종대왕</td>
                    <td>세종시 대왕구</td>
                    <td>30평</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

</html>

웹기능을 만들때 저장하기와 불러오기 기능을 구현해야한다면 저장하기를 먼저 구현해라, 불러오기를 먼저 구현하면 데이터가 없기때문에 잘 적용이 된건지 확인하기가 어렵다.

데이터 저장하기

<app.py>

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.6k98ulg.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/mars", methods=["POST"])
def mars_post():
    name_receive = request.form['name_give']
    address_receive = request.form['address_give']
    size_receive = request.form['size_give']

    doc = {
        'name' : name_receive, 
        'address' : address_receive,
        'size' : size_receive
        }
    db.mars.insert_one(doc)

    return jsonify({'msg':'저장완료!'})

@app.route("/mars", methods=["GET"])
def mars_get():
    return jsonify({'msg':'GET 연결 완료!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

데이터 저장을 위해 MongoDB에 저장할 수 있게 해주는 import를 작성했다.
mars_post 함수를 변경해 이름, 주소, 평수를 받을 수 있게 수정하고 데이터가 등록될경우 ‘저장완료’ 메시지가 나오도록 했다.

<index.html>

...생략

function save_order() {
            let name = $('#name').val()
            let address = $('#address').val()
            let size = $('#size').val()

            let formData = new FormData()
            formData.append("name_give", name)
            formData.append("address_give", address)
            formData.append("size_give", size)

            fetch('/mars', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data["msg"])
                window.location.reload()
            }

...생략

<button onclick="save_order()" type="button" class="btn btn-warning mybtn">
                주문하기
					</button>

...생략

주문하기 버튼을 눌렀을 시에 save_order 함수가 실행되니 해당 함수를 수정한다.
이름 주소 평수를 입력 받으면 백엔드로 정보를 보내게 만들고 벡엔드가 해당 정보를 받으면 메시지를 프론트로 보내서 해당 메시지를 alert로 띄워지며 창을 새로고침하게 만들었다.

데이터 불러오기

<app.py>

...생략

@app.route("/mars", methods=["GET"])
def mars_get():
    mars_data = list(db.mars.find({},{'_id':False}))
    return jsonify({'result': mars_data})

...생략

프론트에서 데이터 요청이 왔을때 db에 있는 모든 데이터를 아이디값을 제외하고 리턴한다.

<index.html>

...생략

$(document).ready(function () {
            show_order();

...생략

function show_order() {
            fetch('/mars').then((res) => res.json()).then((data) => {
                let rows = data['result']
                $('#order-box').empty()
                rows.forEach((a) => {
                    let name = a['name']
                    let address = a['address']
                    let size = a['size']

                    let temp_html = `<tr>
                                        <td>${name}</td>
                                        <td>${address}</td>
                                        <td>${size}</td>
                                    </tr>`
                    $('#order-box').append(temp_html)

...생략

<tbody id="order-box">
                <tr>
                    <td>홍길동</td>
                    <td>서울시 용산구</td>
                    <td>20</td>
                </tr>

...생략

처음 페이지가 시작될때 show_order 함수를 실행한다.

show_order 함수의 rows변수는 data의 result키를 넣은 값을 반환받는다.

order-box에 값을 추가해 나갈것이기 때문에 기존에 있던 데이터를 없앤다.

반목문을 돌려 rows의 값을 하나씩 a변수에 저장시키고 이름,주소,평수를 각각 정의한다.

그리고 temp_html을 임시저장소로 만들고 order-box 양식에 맞춰 이름, 주소, 평수를 저장한다.

그리고 order-box에 temp_html의 값을 추가하는것으로 반복문의 한사이클이 끝난다.

이처럼 원래 있던 데이터는 없어지고 db에 있던 정보가 적히게 되었다.

이로서 화성땅 구매 프로젝트가 마무리 된다.

스파르타피디아 프로젝트

<meta_prac.py>

import requests
from bs4 import BeautifulSoup

url = 'https://movie.naver.com/movie/bi/mi/basic.naver?code=191597'

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(url,headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

ogtitle = soup.select_one('meta[property="og:title"]')['content']
ogdesc = soup.select_one('meta[property="og:description"]')['content']
ogimage = soup.select_one('meta[property="og:image"]')['content']

print(ogtitle, ogdesc, ogimage)

목표로 하는 영화의 url에서 영화제목, 설명, 이미지를 받기위해 작성
프린트를 통해 원하는 대로 출력이 되는지 확인만 하고 코드를 다른곳으로 보낼 예정

<app.py>

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/movie", methods=["POST"])
def movie_post():
    sample_receive = request.form['sample_give']
    print(sample_receive)
    return jsonify({'msg':'POST 연결 완료!'})

@app.route("/movie", methods=["GET"])
def movie_get():
    return jsonify({'msg':'GET 연결 완료!'})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

movie라는 데이터베이스에서 데이터를 가져오기 위해 작성

<index.html>

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
        crossorigin="anonymous"></script>

    <title>스파르타 피디아</title>

    <link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">

    <style>
        * {
            font-family: 'Gowun Dodum', sans-serif;
        }

        .mytitle {
            width: 100%;
            height: 250px;

            background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://movie-phinf.pstatic.net/20210715_95/1626338192428gTnJl_JPEG/movie_image.jpg');
            background-position: center;
            background-size: cover;

            color: white;

            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
        }

        .mytitle>button {
            width: 200px;
            height: 50px;

            background-color: transparent;
            color: white;

            border-radius: 50px;
            border: 1px solid white;

            margin-top: 10px;
        }

        .mytitle>button:hover {
            border: 2px solid white;
        }

        .mycomment {
            color: gray;
        }

        .mycards {
            margin: 20px auto 0px auto;
            width: 95%;
            max-width: 1200px;
        }

        .mypost {
            width: 95%;
            max-width: 500px;
            margin: 20px auto 0px auto;
            padding: 20px;
            box-shadow: 0px 0px 3px 0px gray;

            display: none;
        }

        .mybtns {
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;

            margin-top: 20px;
        }

        .mybtns>button {
            margin-right: 10px;
        }
    </style>
    <script>
        $(document).ready(function () {
            listing();
        });

        function listing() {
            fetch('/movie').then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }

        function posting() {
            let formData = new FormData();
            formData.append("sample_give", "샘플데이터");

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                console.log(data)
                alert(data['msg'])
            })
        }

        function open_box() {
            $('#post-box').show()
        }
        function close_box() {
            $('#post-box').hide()
        }
    </script>
</head>

<body>
    <div class="mytitle">
        <h1>내 생애 최고의 영화들</h1>
        <button onclick="open_box()">영화 기록하기</button>
    </div>
    <div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>
        <div class="mybtns">
            <button onclick="posting()" type="button" class="btn btn-dark">기록하기</button>
            <button onclick="close_box()" type="button" class="btn btn-outline-dark">닫기</button>
        </div>
    </div>
    <div class="mycards">
        <div class="row row-cols-1 row-cols-md-4 g-4" id="cards-box">
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
            <div class="col">
                <div class="card h-100">
                    <img src="https://movie-phinf.pstatic.net/20210728_221/1627440327667GyoYj_JPEG/movie_image.jpg"
                        class="card-img-top">
                    <div class="card-body">
                        <h5 class="card-title">영화 제목이 들어갑니다</h5>
                        <p class="card-text">여기에 영화에 대한 설명이 들어갑니다.</p>
                        <p>⭐⭐⭐</p>
                        <p class="mycomment">나의 한줄 평을 씁니다</p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

백앤드

<app.py>

from flask import Flask, render_template, request, jsonify
app = Flask(__name__)

from pymongo import MongoClient
client = MongoClient('mongodb+srv://sparta:test@cluster0.6k98ulg.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta

import requests
from bs4 import BeautifulSoup

@app.route('/')
def home():
    return render_template('index.html')

@app.route("/movie", methods=["POST"])
def movie_post():
    url_receive = request.form['url_give']
    commet_receive = request.form['comment_give']
    star_receive = request.form['star_give']

    headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
    data = requests.get(url_receive,headers=headers)

    soup = BeautifulSoup(data.text, 'html.parser')

    ogtitle = soup.select_one('meta[property="og:title"]')['content']
    ogdesc = soup.select_one('meta[property="og:description"]')['content']
    ogimage = soup.select_one('meta[property="og:image"]')['content']

    doc = {
        'title' : ogtitle,
        'desc' : ogdesc,
        'image' : ogimage,
        'comment' : commet_receive,
        'star': star_receive
    }
    db.movies.insert_one(doc)

    return jsonify({'msg':'저장완료!'})

@app.route("/movie", methods=["GET"])
def movie_get():
    all_movies = list(db.movies.find({},{'_id':False}))
    return jsonify({'result':all_movies})

if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

데이터베이스와 연결하기위해 pymongo 코드 작성

request 함수 사용을 위해 bs4코드 작성

프론트에서 url과 comment, star를 입력 받고 각각 url_receive 와 url_receive, star_receive 변수에 저장한다.

이후 해당 페이지에서 크롤링할 부분의 코드를 분석한뒤 제목, 설명, 이미지 각각 변수를 생성(<meta_prac>에서 가져왔다.)

doc 변수에 딕셔너리 형태로 데이터를 넣고 movies 데이터베이스에 삽입한다.

마지막으로 완료되면 ‘저장완료’라는 메시지를 프론트로 보낸다.

프론트

<index.html>

... 생략

function listing() {
            fetch('/movie').then((res) => res.json()).then((data) => {
                let rows = data['result']
                $('#cards-box').empty()
                rows.forEach((a) => {
                    let comment = a['comment']
                    let title = a['title']
                    let desc = a['desc']
                    let image = a['image']
                    let star = a['star']

                    let star_repeat = '⭐'.repeat(star)

                    let temp_html = `<div class="col">
                                        <div class="card h-100">
                                            <img src="${image}"
                                                class="card-img-top">
                                            <div class="card-body">
                                                <h5 class="card-title">${title}</h5>
                                                <p class="card-text">${desc}</p>
                                                <p>${star_repeat}</p>
                                                <p class="mycomment">${comment}</p>
                                            </div>
                                        </div>
                                    </div>`
                    $('#cards-box').append(temp_html)

                })
            })
        }

        function posting() {
            let url = $('#url').val()
            let comment = $('#comment').val()
            let star = $('#star').val()

            let formData = new FormData();
            formData.append("url_give", url);
            formData.append("comment_give", comment);
            formData.append("star_give", star);

            fetch('/movie', { method: "POST", body: formData }).then((res) => res.json()).then((data) => {
                alert(data['msg'])
                window.location.reload()
            })
        }

...생략

<div class="mypost" id="post-box">
        <div class="form-floating mb-3">
            <input id="url" type="email" class="form-control" placeholder="name@example.com">
            <label>영화URL</label>
        </div>
        <div class="input-group mb-3">
            <label class="input-group-text" for="inputGroupSelect01">별점</label>
            <select class="form-select" id="star">
                <option selected>-- 선택하기 --</option>
                <option value="1"></option>
                <option value="2">⭐⭐</option>
                <option value="3">⭐⭐⭐</option>
                <option value="4">⭐⭐⭐⭐</option>
                <option value="5">⭐⭐⭐⭐⭐</option>
            </select>
        </div>
        <div class="form-floating">
            <textarea id="comment" class="form-control" placeholder="Leave a comment here"></textarea>
            <label for="floatingTextarea2">코멘트</label>
        </div>

...생략

listing과 postung 함수를 수정한다.

listing

rows변수에 movies데이터베이스의 result를 키로 갖는 값들이 저장된다.

반복문이 돌기전 cards-box에 있는 데이터를 없애주고 rows를 a변수에 값을 하나씩 집어넣는다.

a의 각 부분마다 변수를 지정해 저장하게 하고 별점의 경우 숫자로 표시되기에 repeat를 이용해 숫자만큼 별을 추가했다.

이후 cards-box의 양식을 temp_html이라는 임시 저장소에 작성하고 해당 부분들에 방금 만든 변수들을 알맞게 집어넣는다.

반복문의 말미에는 cards-box에 temp_html의 값들을 집어넣으며 반복문이 종료 된다.

posting

url, comment, star 변수를 생성해 입력받은 값을 저장한다.

formData에 변수_give를 키로 가지는 값으로 방금만든 변수를 알맞게 입력한다.

이후 백엔드에서 메시지를 보낸것을 이어받아 alert로 창을 띄우고 화면을 새로고침한다.

profile
이제 막 개발 배우는 코린이

0개의 댓글