나만의 단어장 만들기(Web 심화 2)

호호빵·2022년 4월 30일
0

Web

목록 보기
7/12

API 설계

1. 메인페이지

  • 단어를 검색 -> 있으면 아래목록에 하이라이트, 스크롤
                       -> 없으면 단어의 상세페이지로 이동
  • 단어를 클릭 -> 단어의 상세페이지로 이동

2. 상세 페이지

  • 원래 단어는 삭제 버튼, 새 단어는 저장 버튼 노출
  • 저장 버튼을 누르면 DB에 저장하고 삭제버튼으로 바뀜
  • 삭제 버튼을 누르면 DB에서 삭제하고 저장버튼으로 바뀜
  • 예문 박스 만들기는 숙제!

프로젝트 setting

뼈대 준비

  • app.py
main, detail, save_word, delete_word
  • templates
# index - find_word

# detail - get_definition, save_word, delete_word
  • static - 로고, favicon 이미지

상세페이지 만들기(detail.html)

1. 전체 모습 만들기

  • 배경과 배너, favicon 넣기(Open Graph 태그)
  • 단어 뜻 박스 만들기
    (단어, 발음기호, 단어의 타입, 정의, 저장버튼, 삭제버튼)
  • 버튼에 아이콘 붙이기

2. 단어 뜻 가져오기 (get_definition 대신)

  • 먼저 app.py에서 API에 요청을 보내 받은 응답을 보내줌 (서버 먼저)
  • html에서 결과가 들어갈 부분에 jinja2로 표시 (다음 클라이언트)
  • 발음 기호 있는 것만 나타내기
  • 예문이 있는 것만 나타내기
  • 예문의 html태그 잘 나타내고, 깨진 글자 없애기
# OWlbot 요청 API flask
r = requests.get(f"https://owlbot.info/api/v4/dictionary/{keyword}",\
	headers={"Authorization": "Token [내토큰]"})
result = r.json()
print(result)


# 결과 jinja2로 표시
<div class="container">
    <div class="d-flex justify-content-between align-items-end">
        <div>
            <h1 id="word" style="display: inline;">{{ result.word }}</h1>
            <h5 id="pronunciation" style="display: inline;">/{{ result.pronunciation }}/</h5>
        </div>
        <button id="btn-save" class="btn btn-outline-sparta btn-lg">save</button>
        <button id="btn-delete" class="btn btn-sparta btn-lg" style="display:none;">delete</button>
    </div>
    <hr>
    <div id="definitions">
        {% for definition in result.definitions %}
		        <div style="padding:10px">
		            <i>{{ definition.type }}</i>
		            <br>{{ definition.definition }}<br>
		            <span class="example">{{ definition.example }}</span>
		        </div>
        {% endfor %}
    </div>
</div>

3. 새 단어 / 기존 단어 구분

  • 단어에 따라 저장 버튼, 삭제 버튼 보이기
  • 파라미터에서 status_give로 온 파라미터를 다시 status라는 이름으로 템플릿에 보내주기(기본값은 'new')
  • jinja2로 html에서 상황에 맞게 버튼 조절 ({% if %})
status_receive = request.args.get['status_give','new']
return render_template("detail.html", word=keyword, result=result,\
	         			status=status_receive)

4. 저장, 삭제 기능

  • 저장 - save_word()
  • 목록 페이지에서는 단어 당 뜻을 하나만 보여줄 것이기 때문에 단어와 첫 번째 정의만 POST 요청으로 보내고, 서버에서 단어와 뜻을 받아 words 컬렉션에 저장
  • 클라이언트에서는 단어와 첫번째 정의만 POST 요청으로 보냄.
  • 단어 저장에 성공하면 얼럿을 띄운 후, status=old로 바뀐 페이지를 띄움.
  • 저장 버튼에 onclick=save_word()로 연결해줍니다.
# app.py 저장
@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} 저장'})
    
# detail.html 저장
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"
        }
    });
}
  • 삭제 - delete_word()
  • 단어를 삭제할 때는 단어만 있으면 되므로 POST 요청으로 단어를 보내주고, 서버에서는 해당 단어를 찾아 삭제
  • 클라이언트에서는 단어를 보내주고, 단어 삭제에 성공하면 더이상 보여줄 정보가 없으므로 얼럿을 띄운 후 메인 페이지로 이동
# app.py 삭제
@app.route('/api/delete_word', methods=['POST'])
def delete_word():
    # 단어 삭제하기
    word_receive = request.form['word_give']
    db.words.delete_one({"word":word_receive})
    return jsonify({'result': 'success', 'msg': f'word "{word_receive}" deleted'})
    
   
# detail.html 삭제
function delete_word() {
    $.ajax({
        type: "POST",
        url: `/api/delete_word`,
        data: {
            word_give: '{{ word }}',
        },
        success: function (response) {
            alert(response["msg"])
            window.location.href = "/"
        }
    });
}

메인 페이지 만들기 (index.html)

1. 전체 모습 만들기

  • 배경과 배너 넣기
  • 검색창과 테이블 만들기

2. 단어 목록 가져오기

  • jinja2로 단어 목록 가져와서 채워주기
  • app.py에서는 words 컬렉션의 단어들을 가져와 넘기기
  • index.html에서는 각 단어마다 테이블의 한 줄이 되도록 넣어줌
# app.py 
@app.route('/')
def main():
	# DB에서 저장된 단어 찾기
    words = list(db.words.find({}, {"_id": False}))
    return render_template("index.html", words=words)


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

3. 검색 기능 만들기 (find_word())

  • 빈 문자를 검색했을 경우 얼럿 보내기
  • 단어를 검색했을 때 이미 저장된 단어인지 알기 위해서 있는 단어 리스트를 만듦
  • 단어 리스트에 있는 경우에는 해당 행을 하이라이트, 없을 경우 상세페이지로 넘기기
  • 하이라이트된 행은 CSS로 나타냄
# html
<script>
	# 단어 리스트 만들어 놓기
	let words = {{ words|tojson }};  # "같은 것들이 진짜 데이터로 인식하게끔
    let word_list = [];
    for (let i = 0; i < words.length; i++) {
        word_list.push(words[i]["word"])
    }

	fucntion find_word() {
    	let word = $("#input-word").val().toLowerCase();
        if (word == "") {
        	alert("please write something first :)")
            return
        }
        if (word_list.includes(word)) {
				// 리스트에 있으면 하이라이트
            $(`#word-${word}`).addClass('highlight').siblings().removeClass('highlight');
            $(`#word-${word}`).siblings().removeClass("highlight")
            $(`#word-${word}`)[0].scrollIntoView();
        } else {
        	window.locaion.href="/detail/${word}?status_give=new"
        }
    }
</script>

4. 사전에 없는 단어를 검색했을 때

  • API에 없는 단어일 경우 에러가 나면 main으로 리다이렉팅하기
  • 값을 잘 받아왔을 때 상태 코드가 200이므로 200이 아닐 때 main으로 리다이렉팅(값은 r로 받아왔음)
  • 단어 찾기 실패 얼럿을 띄우려면 redirect()에 메시지를 같이 전달
  • main()에서 메시지를 받아 템플릿에 같이 보내줌
  • index.html에서 msg가 있을 때 해당 메시지로 얼럿
# 결과를 못받아와서 상태 코드가 200이 아닌경우
@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 내토큰"})
    if r.status_code != 200:    # 상태코드가 200이 아닌경우, 메세지도 함께
        return redirect(url_for("main", msg="잘못된 단어입니다."))
    result = r.json()
    print(result)
    return render_template("detail.html", word=keyword, result=result, status=status_receive)
    
    
# main에서 메시지를 받아 템플릿에 보내주기
@app.route('/')
def main():
    msg = request.args.get("msg")
    words = list(db.words.find({}, {'_id': False}))
    return render_template("index.html", words=words, msg=msg)

# msg가 있을 때 얼럿 띄우기, index.html에서 
<script>
	{% if msg %}
        alert("{{ msg }}")
    {% endif %}
</script>
profile
하루에 한 개념씩

0개의 댓글