Web 심화 2 - 정적, 동적 웹페이지, jinja2,

호호빵·2022년 4월 29일
0

Web

목록 보기
6/12

정적 웹페이지 vs 동적 웹페이지


동적 웹페이지

  1. CSR(Client-side rendering)
    자바스크립트에 데이터를 포함해서 보낸 후, 클라이언트 쪽에서 HTML을 완성하는 방법
  2. SSR(Server-side rendering)
    서버 쪽에서 템플릿 HTML에 데이터를 끼워넣어 완성된 형태의 HTML을 보내주는 방법
  3. 복합적인 방법
    클라이언트 쪽에서 Ajax 요청을 보내서 서버에서 데이터를 받아와 HTML을 완성하는 방법

Jinja2 언어

Flask 프레임워크에서 사용하는 템플릿 언어
'템플릿'이 되는 HTML 문서에 데이터가 들어갈 곳을 표시해놓는 역할
html에서만 사용 가능

# 서버에서 name 이라는 이름으로 값 보내줌
@app.route('/')
def main():
    myname = "sparta"
    return render_template("index.html", name=myname)
    
# html 파일에서 이 값이 들어갈 자리를 표시
<h3>Hello, {{ name }}!</h3>

# for ,if 문
{% for row in rows %}
    {% set gu_name = row.MSRSTE_NM %}
    {% set gu_mise = row.IDEX_MVL %}
    {% if gu_mise >= 80 %}
    	<li>{{ gu_name }}: {{ gu_mise }}</li>
	{% endif %}
{% endfor %}

Ajax 와 Jinja2

Ajax


# Owlbot API 요청 코드
let word = '{{ word }}'    # keyword로 받아온 변수 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)
           # API에서 받은 값들을 해당 태그에 넣어주기
           # id=word의 값을 받아온 값의 word로 바꿔서 보여줌.
           $("#word").text(response["word"])  # id=word의 값이 name 으로
           $("#pronunciation").text(`/${response["pronunciation"]}/`)
           $("#definitions").empty()
           
           let definitions = response["definitions"] # dic 형태
           for (let i=0;i<definitions.length;i++) {
               let definition = definitions[i]
               let html_temp = `<div style="padding:10px">
                                   <i>${definition["type"]}</i>
                                   <br>${definition["definition"]}<br>
                                   <span class="example">${definition["example"]}</span>
                               </div>`
           $("#definitions").append(html_temp)
	       }
       }
   })
}
# -------------      발음이 null 이라면       ----------------------
if (response["pronunciation"]==null) {
   $("#pronunciation").text("")
} else {
   $("#pronunciation").text(`/${response["pronunciation"]}/`)
}
# --------------     예문이 null이라면        ----------------------
let html_temp = ``
if (definition["example"]!=null) {
   html_temp = `<div style="padding:10px">
                   <i>${definition["type"]}</i>
                   <br>${definition["definition"]}<br>
                   <span class="example">${definition["example"]}</span>
               </div>`
} else {
   html_temp = `<div style="padding:10px">
                   <i>${definition["type"]}</i>
                   <br>${definition["definition"]}<br>
               </div>`
}

$("#definitions").append(html_temp)

Jinja2


# app.py에서 요청을 보내 받은 응답을 보여줌
@app.route('/detail/<keyword>')
def detail(keyword):
r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",\
	headers={"Authorization": "Token [내토큰]"})
result = r.json()
print(result)
return render_template("detail.html", word=keyword, result=result)


# detail.html의 원래 div에 변형
<div id="definitions">
    {% for definition in result.definitions %}
	<div style="padding:10px">
		<i>{{ definition.type }}</i>   {{ definition['type'] }} 도 가능
		<br>{{ definition.definition }}<br>
		<span class="example">{{ definition.example }}</span>
	</div>
    {% endfor %}
</div>
    
# 1. example 이 none 일 때
{% if definition.example != None %}
<span class="example">{{ definition.example }}</span>
{% endif  %}			# {% if definition.example %} 도 가능 

# 2. 예문에 html 태그가 있을 때 
<span class="example">{{ definition.example|safe }}</span>

# 3. 예문에서 깨진 문자나 기호가 있을 때
{{ definition.example.encode('ascii', 'ignore').decode('utf-8') }}
# (인코드할 때, ascii 문자를 무시하고, 다시 문자로 인식하기)

# 최종 div 변형
<div id="definitions">
   {% for definition in result.definitions %}
    <div style="padding:10px">
        <i>{{ definition.type }}</i>
        <br>{{ definition.definition.encode('ascii', 'ignore').decode('utf-8') }}<br>
        {% if definition.example %}
        <span class="example">{{ definition.example.\
        encode('ascii', 'ignore').decode('utf-8')|safe }}</span>
        {% endif %}
    </div>
    {% endfor %}
</div>

나만의 단어장 만들기

전체 배경, 배너, favicon, og 넣기

# css
.wrap {
    background-color: RGBA(232, 52, 78, 0.2);
    min-height: 100vh;
    padding-bottom: 50px;
}

.banner {
    width: 100%;
    height: 200px;

    background-color: white;
    background-image: url('{{ url_for("static", filename="logo_red.png") }}');

    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;

    cursor: pointer;
}

# html
<div class="wrap">
	<div class="banner" onclick="window.location.href='/'">
</div>

# head 위쪽에
<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">

URL 일부를 변수로 받기

  • 브라우저에 HTML을 띄우는 것은 GET 요청이기 때문에, 주소 뒤에 ?를 붙여 파라미터를 넘겨줄 수 있음
# 브라우저에 해당 API로 요청 보내기
http://localhost:5000/detail?word_give=hello

# 서버에서 파라미터 값을 받아 html로 넘겨주기;
@app.route('/detail')
def detail():
    word_receive = request.args.get("word_give")
    return render_template("detail.html", word=word_receive)
    
# html에서 word라는 변수에 저장된 값 나타내기
받은 단어는 {{ word }}

# flask 프레임워크에서는 url의 일부를 변수로 받을 수도 있음
@app.route('/detail/<keyword>')
def detail(keyword):
    return render_template("detail.html", word=keyword)


API에 요청 보내기

  1. 파이썬으로
  2. Ajax 코드로
# 파이썬의 app.py에서
r = requests.get("https://owlbot.info/api/v4/dictionary/owl", headers={"Authorization": "Token [내토큰]"})
result = r.json()
print(result)

# html의 Ajax 코드로
$.ajax({
    type: "GET",
    url: "https://owlbot.info/api/v4/dictionary/owl",
    beforeSend: function (xhr) {
        xhr.setRequestHeader("Authorization", "Token [내토큰]");
    },
    data: {},b
    error: function (xhr, status, error) {
        alert("에러 발생!");
    },
    success: function (response) {
        console.log(response)
    }
})

새 단어/기존 단어 구분

# status_receive 추가
@app.route('/detail/<keyword>')
def detail(keyword):
    status_receive = request.args.get('status_give')
    # API에서 단어 뜻 찾아서 결과 보내기
    r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}", headers={"Authorization": "Token 23b152dbd0945a866144590f5aa4c6a2c8df3db3"})
    result = r.json()
    print(result)
    return render_template("detail.html", word=keyword, result=result, status=status_receive)
    


단어 저장, 삭제

  • 디비에는 컬렉션이 있고 , 컬렉션 안의 각 한줄을 다큐먼트, 그 각각의 열은 필드
  • 목록 페이지에서는 단어 당 뜻을 하나만 보여줄 것이기 때문에 단어와 첫 번째 정의만 POST 요청으로 보내고, 서버에서 단어와 뜻을 받아 words 컬렉션에 저장
  • 클라이언트에서는 단어와 첫번째 정의만 POST 요청으로 보냄.
  • 단어 저장에 성공하면 얼럿을 띄운 후, status=old로 바뀐 페이지를 띄움.
  • 저장 버튼에 onclick=save_word()로 연결

받아온 단어를 리스트에 저장해두기


# app.py
@app.route('/')
def main():
    # DB에서 저장된 단어 찾아서 HTML에 나타내기
    words = list(db.words.find({}, {'_id': False}))
    return render_template("index.html", words=words)
    
# html
  <script>	# words가 리스트 형식이라 ""를 인식못해서 tojson 으로 문자로 인식하게 해주기
        let words = {{ words|tojson }};
        let word_list = [];
        for (let i=0; i<words.length; i++) {
            word_list.push(words[i]['word'])
        }
        console.log(word_list)
    </script>

찾은 단어에 하이라이트

# 단어를 하이라이트하고 스크롤해서 그 자리를 찾아가게 해라 
# (형제들은 클래스에서 빼버려라, 하나만 하이라이트 되게)
$(`#word-${word}`).addClass("highlight")
$(`#word-${word}`).siblings().removeClass("highlight")
$(`#word-${word}`)[0].scrollIntoView()

숙제 - 예문 박스 만들기

function get_examples() {
            $("#example-list").empty()
            $.ajax({
                type: "GET",
                url: `/api/get_exs?word_give=${word}`,
                data: {},
                success: function (response) {
                    console.log(response)
                    let examples = response['examples']
                    for (let i=0; i < examples.length; i++) {
                        let example = examples[i]

                        let temp_html = `<li id="ex-${i}}">${example['example']}&nbsp;&nbsp;&nbsp;<a
                                         href="javascript:delete_ex(${i})">delete</a></li>`

                        $('#example-list').append(temp_html)
                    }


                }
            });
        }

        function add_ex() {
                let new_ex = $('#new-example').val();
                if (!new_ex.toLowerCase().includes(word)) {
                    alert(`the word '${word}' is not included.`);
                    return;
                }
                console.log(new_ex)
                $.ajax({
                    type: "POST",
                    url: `/api/save_ex`,
                    data: {
                        word_give: word,
                        example_give: new_ex
                    },
                    success: function (response) {
                        get_examples();
                        $('#new-example').val("");
                    }
                });


        }

        function delete_ex(i) {
            console.log("deleting", i)
            $.ajax({
                type: "POST",
                url: `/api/delete_ex`,
                data: {
                    word_give: word,
                    number_give: i
                },
                success: function (response) {
                    get_examples()
                }
            });
        }
        
        
        
        
@app.route('/api/get_exs', methods=['GET'])
def get_exs():
    # 예문 가져오기
    word_receive = request.args.get('word_give')
    result = list(db.examples.find({'word': word_receive}, {'_id': False}))

    return jsonify({'result': 'success', 'examples': result})

@app.route('/api/save_ex', methods=['POST'])
def save_ex():
    word_receive = request.form['word_give']
    example_receive = request.form['example_give']

    doc = {
        'word': word_receive,
        'example': example_receive
    }

    db.examples.insert_one(doc)
    return jsonify({'result': 'success', 'msg': '예문 저장'})


@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'})        
profile
하루에 한 개념씩

0개의 댓글