본문 바로가기

웹 개발/웹개발플러스

웹 개발 플러스 2 - 나만의 단어장

0. 동적 웹 페이지, 정적 웹 페이지

정적 웹 페이지란 서버에 저장되어 있는 HTML + CSS 파일을 그대로 보여 주는 것입니다. 

반면 동적 웹 페이지는 데이터의 추가/ 가공을 통해 저장된 HTML의 모습이 변합니다.

 

정적인 웹 페이지는 추가적 통신, 계산이 필요가 없기 때문에 속도가 빠르고 서버의 부담이 적지만 추가, 수정, 삭제 등의 내용 변경이 필요하면 HTML 자체를 수정해야 하기 때문에 번거로우며 서비스가 한정적입니다. 따라서 변화가 적은 회사 소개, 음식메뉴, 포트폴리오 등이 정적인 웹 페이지로 만들기에 적합합니다. 

 

반면 동적 웹 페이지는 상황, 시간, 사용자의 요청에 따라 다른 모습을 보여줘야 합니다. 하지만 보안에 취약하고 모습이 계속 변하기 때문에 검색엔진 최적화(SEO, Search Encine Optimazation)가 어렵습니다. 오늘의 날씨, 블로그, 게시판 등 오늘날 많은 페이지는 동적 웹페이지로 만들어집니다. 

 

동적인 웹 페이지에는 CSR, SSR, 복합 방식이 있습니다.

- CSR (Client-side rendering) : 자바스크립트에 데이터를 포함시켜 응답을 받은 후 클라이언트 쪽에서 HTML을 완성합니다.
- SSR (Server-side rendering) : 서버 쪽에서 템플릿 HTML에 데이터를 끼워 넣어 완성된 형태의 HTML을 보내주는 방법.
- 복합 : 처음에 요청이 오면 프론트 엔드 서버로 html에 자바스크립트를 심어서 보내주고, 그다음 페이지가 뜨고 나서 자바 스크립트 코드로부터 ajax로 요청을 해 백엔드 서버로부터 데이터를 받아와 채워 넣는 방식입니다. 

 

이제까지 프로젝트에서는 복합적인 방법으로 동적 웹 페이지를 구현했습니다. 본 프로젝트는 jinja2 템플릿을 활용해서 Flask 프레임워크에서 SSR을 구현해 보도록 하겠습니다. 

 

또한 API 키에대해 학습해 보도록 하겠습니다. API 키는 Open API 요청의 남용을 막기 위해 허가 받는 방식입니다. 

 

구현 목표 : 나만의 단어장 (http://spartacodingclub.shop/wp/vocab)

 

 

1. 플라스크를 활용한 멀티페이지 사이트 개발 - 기초

패키지 : flask, jinja2, pymongo, requests

멀티페이지를 이동하는 방법 : a 태그의 href 속성은 HTTP GET 요청과 동일하게 작동합니다. Ancher 태그의 하이퍼링크 속성에 주소인 '/detail' 을 작성하면 정의된 app route를 통해 detail.html 페이지가 렌더링되어 반환됩니다. 

<a href="/detail">상세 페이지로 가기</a>
@app.route('/detail')
def detail():
    return render_template("detail.html")

또는 자바스크립트에서는 window.location.href 객체를 활용해 href 값을 변경하거나 XMLHttpRequest 객체를 사용해 ajax 요청을 보낸 등 링크와 유사한동작을 수행할 수 있습니다.  

function to_main() {
    window.location.href = "/"
}

위의 코드는 url 'http://localhost:5000/'의 메인 페이지로 변경합니다. 

 

2. jinja2 템플릿 언어 

서버에서 name 이라는 이름으로 값을 보낼 경우 html에서 바로 이 값을 표시할 수 있습니다.

@app.route('/')
def main():
    myname = "sparta"
    return render_template("index.html", name=myname)
<h3>Hello, {{ name }}!</h3>

또는 서버에서 리스트 형태의 데이터를 반복문과 조건문을 통해 가공해 처리할 수 있습니다. 예를들어 다음과 같이 flask 서버로부터 index.html을 렌더링할 당시에 리스트 형태의 데이터를 data라는 이름에 함께 싣고 전송한다고 가정하겠습니다. 아래에 보시다싶이 키값으로 "gu"와 "ms"에 각각 자치구와 미세먼지 농도를 표시하고 있습니다.

@app.route('/')
def main():
	list_data = [{ "gu" : "종로구", "ms" : 50 },
        { "gu" : "동작구", "ms" : 60 },
        { "gu" : "마포구", "ms" : 64 },
        { "gu" : "서초구", "ms" : 61 },
        { "gu" : "강남구", "ms" : 63 }]
    return render_template("index.html", data=list_data)

이를 jinja2 템플릿을 활용해 모든 요소에 대하여 변수 gu_name, gu_ms를 설정하고 만약 gu_ms 값이 60 보다 큰 경우에만 해당 내용을 페이지에 표시할 수 있습니다. 

<ul id="gu-list">
	{% for d in data  %}
    	{% set gu_name = d["gu"] %}
        {% set gu_ms = d["ms"] %}
        {% if gu_ms >= 60 %}
        	<li> {{ gu_name }} : {{ gu_ms }}</li>
        {% endif %}
    {% endfor %}
	<li>{{ }}</li>
</ul>

이 밖에도 jinja2 템플릿은 head 태그 안의 내용을 바꿀 수 있고 html 문서를 통째로 가져와 사용할 수도 있습니다. 

 

 

3. GET 요청 시 데이터를 FLASK 서버로 데이터 전달하기

GET 요청 시 URL에 변수를 함께 담아 보낼 수 있습니다. "?" 뒤에 오는 파리미터들은 Query String 이라고 하며 예를들어아래와 같은 주소가 브라우저에서 GET 요청을 보내진다면 플라스크 서버에서는 다음과 같은 형태로 파라미터 값을 가져올 수 있습니다.

http://localhost:5000/detail?word_give=hello
@app.route('/detail')
def detail():
    word_receive = request.args.get("word_give")
    return render_template("detail.html", word=word_receive)

혹은

@app.route('/detail/<keyword>')
def detail(keyword):
    return render_template("detail.html", word=keyword)

위의 예시와 같이 플라스크 프레임워크에서는 URL 일부를 변수로서 받을 수도 있습니다. 

 

 

4. 프로젝트 셋팅 

기능 명세 : 
단어 저장 기능 :  만약 이미 DB에 단어가 존재할 경우 삭제버튼을 노출시키며 아닐경우 저장버튼을 노출
단어 삭제 기능 : 삭제 버튼을 누르면 DB에서 삭제 후 메인페이지로 이동
단어 검색 기능 : 단어장에 이미 있는 경우 하이라이트, 해당 단어를 클릭 시 상세 페이지 이동
예문 저장 삭제 : 저장된 단어의 경우 예문 칸이 보여지며 맨 아래로 추가, 예문에 단어가 포함되지 않을 경우 얼럿, 삭제도 가능하게 함

 

5. 외부 API 받아오기 - "owlbot.info"

requests 패키지를 활용할 경우 : 

r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",
                 headers={"Authorization": "Token [내토큰]"})
result = r.json()
print(result)

jquery ajax를 활용할 경우 : 

let word = '{{ word }}'
$(document).ready(function () {
    get_definitions()
})

function get_definitions() {
    $.ajax({
        type: "GET",
        url: `https://owlbot.info/api/v4/dictionary/${word}`,
        beforeSend: function (xhr) {
            xhr.setRequestHeader("Authorization", "Token [내토큰]");
        },
        data: {},
        error: function (xhr, status, error) {
            alert("에러 발생!");
        },
        success: function (response) {
            console.log(response)
        }
    })
}

 

 

ajax 를 활용해 해당 단어를 요청해 페이지를 렌더링 하는 것은 기존에 프로젝트에서 많이 숙달되었기 때문에 본 프로젝트에서는 생략하도록 하겠다. 

 

하지만 ajax를 활용해서 페이지를 렌더링한 후 js 코드를 활용해 해당 html 코드들을 붙여 넣는 방식은 페이지 로딩에 시간 차이를 일으킨다. ajax를 보내서 사전 API 데이터를 받아오고 채워 넣기까지 시간이 걸리기 때문이다. 

 

이는 사용자 입장에서 이상함을 느끼게 한다. 

 

단어장을 한 번에 띄우려면 Jinja2 템플릿을 활용해서 html 을 완성해서 보내주는 Server-Side-Rendering을 해주는 것이 훨씬 좋다. 

 

6. Jinja2 템플릿을 활용해서 나만의 단어장 구현하기

flask 서버에서 json으로 가져온 데이터를 result란 이름으로 렌더링을 했다.

<body>
    <div class="wrap">
        <div class="banner" onclick="window.location.href = '/'">
        </div>
        <div class="container">
            <div class="d-flex justify-content-between align-items-end">
                <div>
                    {% set word = result.word %}
                    {% set pronunciation = result.pronunciation %}
                    <h1 id="word" style="display: inline;">{{ word }}</h1>
                    {% if pronunciation != None %}
                    <h5 id="pronunciation" style="display: inline;">/{{ pronunciation }}/</h5>
                    {% endif %}
                </div>

                <button id="btn-save" class="btn btn-outline-sparta btn-lg">
                    save <i class="fa fa-floppy-o" aria-hidden="true"></i>
                </button>
                <button id="btn-delete" class="btn btn-sparta btn-lg">
                    delete <i class="fa fa-trash-o" aria-hidden="true"></i>
                </button>
            </div>
            <hr>
            {% for defi in result.definitions %}
                {% set definition = defi.definition %}
                {% set example = defi.example %}
                {% set type = defi.type %}
                <div id="definitions">
                    <div style="padding:10px">
                        <i>{{ type }}</i>
                        <br>{{ definition.encode('ascii', 'ignore').decode('utf-8') }}<br>
                        {% if example != None %}
                        <span class="example">{{ example.encode('ascii', 'ignore').decode('utf-8') | safe }}</span>
                        {% endif %}
                    </div>
                </div>
            {% endfor %}
        </div>
    </div>
</body>

불필요한 문자열이나 인코딩할 수 없는 문자열 혹은 태그 등을 방지하기 위해서 아래와 같은 문법을 활용한다. 

<span class="example">{{ example.encode('ascii', 'ignore').decode('utf-8') | safe }}</span>

위의 예시는 해당 문자열을 'ascii'로 인코딩을 하며 인코딩할 수 없는 문자열은 'ignore'하고 다시 'utf-8'로 디코딩한다는 뜻이다. 또한 태그 등은 안전하다고 표시한다.

 

7. jinja2 템플릿에서 리스트와 딕셔너리로 이루어진 데이터를 자바 스크립트에 받기 

파이썬에서 딕셔너리와 리스트로 구성된 데이터는 javascript로 전달되면서 문자 &#39 홀따옴표(') 그대로 전달받기 때문에 자료형으로써 사용할 수 없다. 이럴 때 jinja2에서는 (|) 파이프에 tojson을 명시함으로서 해당 문자열 데이터를 json 데이터로서 사용할 수 있다. 

let words = {{ words | tojson}}

 

 

8. 하이라이트와 스크롤 기능 넣기

$(`#word-${word}`).addClass("highlight")
$(`#word-${word}`)[0].scrollIntoView()
tr.highlight > td {
    background-color: red;
}

 

9. 형제 요소들의 클래스 요소 없애기

$(`#word-${word}`).siblings().removeClass("highlight")

 

10. og 태그와 favicon

<meta property="og:title" content="Sparta Vocabulary Notebook"/>
<meta property="og:description" content="mini project for Web Plus"/>
<meta property="og:image" content="{{ url_for('static', filename='logo_red.png') }}"/>
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">

 

11. 구현

templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Sparta Vocabulary Notebook</title>

    <meta property="og:title" content="Sparta Vocabulary Notebook"/>
    <meta property="og:description" content="mini project for Web Plus"/>
    <meta property="og:image" content="{{ url_for('static', filename='logo_red.png') }}"/>
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
          crossorigin="anonymous">
    <!-- FontAwesome CSS-->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="../static/mystyle.css">
    <style>
        .search-box {
            width: 70%;
            margin: 50px auto;
            max-width: 700px;
        }

        .table {
            width: 80%;
            max-width: 800px;
            margin: auto;
            table-layout: fixed;
        }

        .table th {
            border-top-style: none;
        }

        td {
            background-color: white;
            text-overflow: ellipsis;
            overflow: hidden;
            white-space: nowrap;
        }

        td > a, a:visited, a:hover, a:active {
            color: black;
        }

        thead:first-child tr:first-child th:first-child {
            border-radius: 10px 0 0 0;
        }

        thead:first-child tr:first-child th:last-child {
            border-radius: 0 10px 0 0;
        }

        tbody:last-child tr:last-child td:first-child {
            border-radius: 0 0 0 10px;
        }

        tbody:last-child tr:last-child td:last-child {
            border-radius: 0 0 10px 0;
        }

        tr.highlight > td {
            background-color: red;
            color: white;
        }
        tr.highlight > td > a {
            color: white;
        }
    </style>
    <script>
        {% if msg != None %}
            alert("{{ msg }}")
            window.location.href = "/"
        {% endif %}
        let words = {{ words | tojson }};
        let word_list = [];
        for (let i = 0; i < words.length; i++){
            word_list.push(words[i]["word"])
        }

        function find_word(){
            let word = $('#input-word').val().toLowerCase();
            if (word == ""){
                alert("값을 입력해주세요")
                return
            }

            if (word_list.includes(word)){
                $(`#word-${word}`).addClass("highlight")
                $(`#word-${word}`).siblings().removeClass("highlight")
                $(`#word-${word}`)[0].scrollIntoView()
            } else {
                window.location.href = `/detail/${word}?status_give=new`
            }
        }

    </script>
</head>
<body>
    <div class="wrap">
        <div class="banner" onclick="window.location.href = '/'"></div>
        <div class="search-box d-flex justify-content-center">
            <input id="input-word" class="form-control" style="margin-right: 0.5rem">
            <button class="btn btn-light" onclick="find_word()"><i class="fa fa-search"></i></button>
        </div>
        <table class="table">
            <thead class="thead-light">
            <tr>
                <th scope="col" style="width:30%">WORD</th>
                <th scope="col">MEANING</th>

            </tr>
            </thead>
            <tbody id="tbody-box">
            {% for word in words %}
                <tr id="word-{{ word.word }}">
                    <td><a href="/detail/{{ word.word }}?status_give=old">{{ word.word }}</a></td>
                    <td>{{ word.definition }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>

 

 

templates/detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Sparta Vocabulary Notebook</title>

    <meta property="og:title" content="Sparta Vocabulary Notebook"/>
    <meta property="og:description" content="mini project for Web Plus"/>
    <meta property="og:image" content="{{ url_for('static', filename='logo_red.png') }}"/>
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
          crossorigin="anonymous">
    <!-- FontAwesome CSS-->
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="../static/mystyle.css">
    <style>
        .container {
            width: 80%;
            max-width: 800px;
            margin: 30px auto;
            padding: 20px;
            background-color: white;

            border: solid 1px gray;
            border-radius: 10px;
        }

        span.example {
            color: gray;
            font-size: 14px;
        }

        .btn-sparta {
            color: #fff;
            background-color: #e8344e;
            border-color: #e8344e;
        }

        .btn-outline-sparta {
            color: #e8344e;
            background-color: transparent;
            background-image: none;
            border-color: #e8344e;
        }
    </style>
    <script>
        $(document).ready(function () {
            {% if status == "old" %}
            get_examples();
            {% endif %}
        })

        function save_word() {
            $.ajax({
                type: "POST",
                url: `/api/save_word`,
                data: {
                    word_give: "{{ word }}",
                    definition_give: "{{ result.definitions[0].definition }}"
                },
                success: function (response) {
                    alert(response["msg"])
                    window.location.href = "/detail/{{ word }}?status_give=old"
                }
            });
        }

        function delete_word() {
            $.ajax({
                type: "POST",
                url: `/api/delete_word`,
                data: {
                    word_give: "{{ word }}"
                },
                success: function (response) {
                    alert(response["msg"])
                    window.location.href = "/"
                }
            });
        }

        function get_examples() {
            $("#example-list").empty()
            let word = "{{ word }}"
            $.ajax({
                type: "GET",
                url: `/api/get_exs?word_give=${word}`,
                data: {},
                success: function (response) {
                    let examples = response['examples']
                    for (let i = 0; i < examples.length; i++) {
                        let ex = examples[i]['example']
                        let id = examples[i]['number']
                        let temp_html = `<li id="ex-${id}">${ex}&nbsp;&nbsp;&nbsp;
                                            <a href="javascript:delete_ex(${id})">delete</a></li>`
                        $('#example-list').append(temp_html)
                    }
                }
            });
        }

        function add_ex() {
            let new_ex = $('#new-example').val();
            let word = '{{ word }}'
            let number = $('#example-list').children().length + 1

            if (new_ex.toLowerCase().includes(word.toLowerCase())) {
                $.ajax({
                    type: "POST",
                    url: `/api/save_ex`,
                    data: {
                        number_give : number,
                        word_give: word,
                        example_give: new_ex
                    },
                    success: function (response) {
                        console.log(response)
                        get_examples()
                        $('#new-example').val("")
                    }
                });
            } else {
                alert("새로운 예문에 해당 단어가 포함되지 않습니다. ")
            }
        }

        function delete_ex(i) {
            let word = '{{ word }}'

            $.ajax({
                type: "POST",
                url: `/api/delete_ex`,
                data: {
                    word_give: word,
                    number_give: i
                },
                success: function (response) {
                    get_examples()
                }
            });
        }
    </script>

</head>
<body>
    <div class="wrap">
        <div class="banner" onclick="window.location.href = '/'">
        </div>
        <div class="container">
            <div class="d-flex justify-content-between align-items-end">
                <div>
                    {% set word = result.word %}
                    {% set pronunciation = result.pronunciation %}
                    <h1 id="word" style="display: inline;">{{ word }}</h1>
                    {% if pronunciation != None %}
                        <h5 id="pronunciation" style="display: inline;">/{{ pronunciation }}/</h5>
                    {% endif %}
                </div>
                {% if status == "new" %}
                    <button onclick="save_word()" id="btn-save" class="btn btn-outline-sparta btn-lg">
                        save <i class="fa fa-floppy-o" aria-hidden="true"></i>
                    </button>
                {% else %}
                    <button onclick="delete_word()" id="btn-delete" class="btn btn-sparta btn-lg">
                        delete <i class="fa fa-trash-o" aria-hidden="true"></i>
                    </button>
                {% endif %}

            </div>
            <hr>
            {% for defi in result.definitions %}
                {% set definition = defi.definition %}
                {% set example = defi.example %}
                {% set type = defi.type %}
                <div id="definitions">
                    <div style="padding:10px">
                        <i>{{ type }}</i>
                        <br>{{ definition.encode('ascii', 'ignore').decode('utf-8') }}<br>
                        {% if example != None %}
                            <span class="example">{{ example.encode('ascii', 'ignore').decode('utf-8') | safe }}</span>
                        {% endif %}
                    </div>
                </div>
            {% endfor %}
        </div>
        {% if status == "old" %}
            <div id="examples" class="container">
                <h3 style="text-align: center;margin-bottom:1rem">Write your own sentences!</h3>
                <ul id="example-list">
                    <li id="ex-0">This sentence contains the word 'word'.&nbsp;&nbsp;&nbsp;<a
                            href="javascript:delete_ex(0)">delete</a></li>
                    <li id="ex-1">I don't like using the MS Word program.&nbsp;&nbsp;&nbsp;<a
                            href="javascript:delete_ex(1)">delete</a></li>
                </ul>
                <div class="d-flex justify-content-between" style="margin-left:20px;">
                    <input id="new-example" class="form-control form-control-sm" style="margin-right: 0.5rem">
                    <button class="btn btn-outline-secondary btn-sm" onclick="add_ex()">add</button>
                </div>
            </div>
        {% endif %}
    </div>
</body>
</html>

 

app.py 

import requests
from flask import Flask, render_template, jsonify, request, redirect, url_for
from pymongo import MongoClient

app = Flask(__name__)

client = MongoClient('[내 AWS 아이피]', 27017, username="[아이디]", password="[비밀번호]")
db = client.dbsparta_plus_week2


@app.route('/')
def main():
    # DB에서 저장된 단어 찾아서 HTML에 나타내기
    msg = request.args.get("msg")
    words_list = list(db.words.find({}, {"_id": False}))
    return render_template("index.html", words=words_list, msg=msg)


@app.route('/detail/<keyword>')
def detail(keyword):
    status_receive = request.args.get("status_give")

    r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",
                     headers={"Authorization": "Token b5d4eac085aead89362767df4aa0d1b47b07b7f2"})
    if r.status_code != 200:
        return redirect(url_for("main", msg="단어가 이상합니다."))

    result = r.json()

    return render_template("detail.html", word=keyword, result=result, status=status_receive)


@app.route('/api/save_word', methods=['POST'])
def save_word():
    # 단어 저장하기
    word_receive = request.form["word_give"]
    definition_receive = request.form["definition_give"]
    doc = {
        "word": word_receive,
        "definition": definition_receive
    }
    db.words.insert_one(doc)
    return jsonify({'result': 'success', 'msg': f'{word_receive} 저장'})


@app.route('/api/delete_word', methods=['POST'])
def delete_word():
    # 단어 삭제하기
    word_receive = request.form['word_give']
    db.words.delete_one({"word": word_receive})
    db.examples.delete_many({"word" : word_receive})

    return jsonify({'result': 'success', 'msg': f'{word_receive} 삭제'})


@app.route('/api/get_exs', methods=['GET'])
def get_exs():
    word = request.args.get("word_give")
    examples = list(db.examples.find({"word" : word}, {"_id": False, "word":False}))
    return jsonify({'examples': examples})


@app.route('/api/save_ex', methods=['POST'])
def save_ex():
    number_receive = request.form["number_give"]
    example_receive = request.form["example_give"]
    word_receive = request.form["word_give"]
    doc = {
        "number" : number_receive,
        "word": word_receive,
        "example": example_receive
    }
    db.examples.insert_one(doc)
    return jsonify({'result': 'success'})


@app.route('/api/delete_ex', methods=['POST'])
def delete_ex():
    word_receive = request.form['word_give']
    number_receive = int(request.form['number_give'])
    example = list(db.examples.find({"word":word_receive}))[number_receive]["example"]
    db.examples.delete_one({"word":word_receive, "example":example})
    return jsonify({'result': 'success'})


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