
flask는 웹 어플리케이션 개발에 사용되는 Python API다. 웹 페이지 개발에 사용되는 비슷한 api인 '장고'에 비해 구조 자체가 간결하여 초보자가 사용하기에 적합하다. flask는 Python에 기반하고 있으나 jinja2 템플릿 엔진을 사용하여 html 파일을 렌더링을 할 수 있도록 해 준다. flask로 어떻게 python 파일(.py)로 정의된 application.py의 변수들을 html 파일에 불러오고, html 파일 내에서 변수들을 어떤 문법으로 사용할 수 있는지 몇 가지 예를 통해 설명해보려고 한다.
from flask import Flask, render_template
import sys
application = Flask(__name__)
@application.route("/")
def hello():
return render_template("index.html")
if __name__ == "__main__":
application.run(host='0.0.0.0')
ㅤ위 코드에서는 기본 화면을 index.html 템플릿을 이용해 보여주고 있다. 만약 홈페이지 주소가 (http://xxx.com/)이라면, 맨 처음 접속했을 때 보여주는 (http://xxx.com/)의 화면이 index.html로 보여진다는 얘기다. 이 화면을 불러오는 함수가 hello()로 정의되어 있는 것을 확인할 수 있다. 라우팅 주소에 따라 화면을 랜더링하는 함수의 이름은 임의로 설정이 가능하나, 한 파일 내의 함수의 이름은 고유해야 한다.
ㅤ
@application.route("/")
def hello():
data=DB.get_data()
return render_template("index.html", datas=data)
ㅤ랜더링하려는 페이지에 필요한 변수는 application.py 파일에서 보내주어야 html 파일 내에서 사용할 수 있다. 이 예제에서는 임의로 설정된 get_data() 함수를 통해 data를 정의하고 있다. DB는 database.py 파일의 클래스 DBhandler()에 의해 정의된 클래스로, get_data() 함수를 내포하고 있다. 이렇게 선언된 data는, render_template() 함수를 통해 'datas'라는 이름의 인자로 보내진다. html 파일에서는 'datas'라는 이름으로 변수를 사용할 수 있으며, 변수의 좌변 이름이 바뀌면 바뀐 이름으로 변수를 사용할 수 있다.
ㅤ
3. html 파일에서 정의된 변수 이름에 따라 변수를 사용한다.
{% for data in datas %}
<p>{{data.res_name}}</p>
ㅤhtml에서 application.py에서 랜더링한 변수를 인식하는 문법을 jinja2 문법이라고 부른다. 중괄호({})를 사용하여 jinja2 문법을 사용한다. 위 예제의 경우, 'datas' 이름으로 받은 변수를 for 문법으로 순회하고 있다. html 파일 내에서, 랜더링으로 넘겨 받은 변수는 {{변수}}의 형태로 인식할 수 있다.
ㅤ
ㅤ팀 프로젝트를 진행하며 가장 많이 사용한 두 가지 jinja2 문법 활용 사례에 대해 소개해보려 한다.
기본 구조
{% if (조건) %}
html 코드
{% elif (조건) %}
html 코드
{% else %}
html 코드
{% endif %}
ㅤ이때 주의할 점은, {% 와 if 사이에 공백이 필수적이라는 사실이다. 조건문과 %} 사이에도 공백이 필수적이다. 또한, {와 %는 붙여 써야 인식 가능하다. if 문을 시작하면 if문이 끝났다는 endif 선언도 필수적이므로, if문 구조를 잘 파악해야 if문을 열고 닫을 때 오류가 생기지 않는다.
ㅤ
- endif가 필요한 부분에 없으면 endif가 없다는 오류를 받게 된다.
ㅤjinja2.exceptions.TemplateSyntaxError: Unexpected end of template. Jinja was looking for the following tags: 'endif'. The innermost block that needs to be closed is 'if'.
- endif가 필요 없는 부분에 endif가 있으면 알려지지 않은 태그와 마주쳤다는 오류를 받게 된다.
ㅤjinja2.exceptions.TemplateSyntaxError: Encountered unknown tag 'endif'.
조건은 괄호()로 묶어 and나 or을 통해 여러 개 설정이 가능하다.
ㅤex. {% if (a==0) and (b!=0) %}{% endif %}
기본 구조
{% for data in datas %}
{{data.rev_memo}}
{% endfor %}
for문 역시 {% for index in data %}의 띄어쓰기를 잘 지켜야 오류가 발생하지 않는다. 또한 for문이 끝났다는 표시로 {% endfor %}를 붙여줘야 정상적으로 for문이 닫힌다.
ㅤ
ㅤ
ㅤ프로젝트 수행 중 가장 많이 보았던 에러와 해결하는 데에 공을 들였던 에러를 하나씩 소개해 보려고 한다.
ㅤ

ㅤ
ㅤ가장 많이 보았던 에러다. 이는 주로, html 페이지에서 form 태그를 통해 값을 넘겨준 후 연결되는 페이지에서 methods=['POST']가 정의되지 않았을 때 발생한다.
@application.route("/search", methods=['POST'])
def view_search_result():
name = request.form['searchname']
print(name)
data = DB.search_restaurants_byname(name)
tot_count = len(data)
return render_template("search.html", datas=data, name=name, total=tot_count)
ㅤ이렇듯, @application.route("/search", methods=['POST'])로 라우팅 주소와 methods 인자를 ['POST']로 설정하여 넘겨주어야 에러가 발생하지 않는다.
ㅤ
ㅤ가장 처리하기 까다로운 에러 중 하나였다. 에러가 명시적으로 확인되지 않아 일일이 print(data)를 해 보면서 db 구조에 제대로 접근한 것이 맞는지 체크해야 했기 때문에 해결에도 시간을 많이 쏟았다. 기본적으로 firebase가 db의 데이터를 어떻게 관리하는지에 대한 이해가 필요했으므로, 그에 대한 설명을 간단히 하겠다.
ㅤfirebase는 기본적으로 dictionary 형태로 값을 저장한다. 하지만 dictionary 안에서 parent-child 구조를 이룰 수 있어, 트리 구조를 이룰 수 있다는 점을 명심해야 한다. data들이 Node로 정의되어서, 자유롭게 Node를 추가하고 삭제할 수 있다.
ㅤ
ㅤNode는 {key: value} 형식으로 저장된다. key는 필수로 입력해야 한다. value에는 값을 입력하거나, Child Node를 입력해 단말 노드를 추가할 수 있다. Node의 추가는 Firebase 실시간 데이터베이스에서 직접 추가하거나, database를 다루는 database.py 파일에서 self.db.child("key").(child(name).set(data)/push(data))를 입력하여 추가할 수 있다.
ㅤ
ㅤ- set() 함수: 입력하려는 data의 key값을 child() 함수에 입력하는 key값으로 설정할 수 있다.
ㅤㅤex. self.db.child("restaurant").child(name).set(restaurant_info)
ㅤ- push() 함수: 입력하려는 data의 key값이 무작위적이다. 이때, node 자체가 생성되는 것이므로 set() 함수와 다르게 child를 생성하는 명령은 생략한다.
ㅤㅤex. self.db.child("review").child(data['res_name']).push(review_info)
ㅤfirebase는 기본적으로 dictionary 형태로 값을 반환한다. 하지만 어떤 함수로 값을 반환하냐에 따라 list인지, dictionary인지 사소한 차이가 발생한다. firbase에서 값을 반환할 때 사용하는 python 함수로는 get(), val(), items()가 있다.
특정 주소에 저장된 object 반환
ex.
<pyrebase.pyrebase.PyreResponse object at 0x7f484440a410>
ㅤobject 형태로 반환되어 dictionary, list 관련 함수의 사용이 힘들다. 대신 Pyrebase 내장함수인 each(), key() val() 사용이 가능하다.
Dictionary 형태로 반환
{key1:value1{}, key2:value2{}}
{'-NJFgJWvH5pbTdxdQ1Vs':
{'id': 'ewhain4', 'img_path': '서왕만두군만두.jpg', 'res_name': '미스터서왕만두', 'rev_amount': '적당함', 'rev_etc': ['음식이 맛있음'], 'rev_headcount': '혼자', 'rev_memo': '바삭바삭 맛있어요. 생각보다 양도 많습니다. ', 'rev_menu': '군만두', 'rev_name': '이모조모', 'rev_price': '7000', 'rev_score': '4', 'rev_time': '저녁', 'rev_vegan': 'X'},
'-NJGPK__Kfz0kON_xKty':
{'id': 'ewhain4', 'img_path': '소룡포.jpg',
'res_name': '미스터서왕만두', 'rev_amount': '적음', 'rev_etc': ['null'], 'rev_headcount': '친구', 'rev_memo' : '육즙이 꽉 차있습니다! 맛있어요. ', 'rev_menu': '소룡포', 'rev_name': '이모조모', 'rev_price': '6500', 'rev_score': '5', 'rev_time': '점심', 'rev_vegan': 'X'}}
ㅤdictionary 관련 함수인 keys(), values(), items() 사용이 가능하다. 그러나 Pyreabase 내장함수(each, val, key)는 사용이 불가하다.
dict_items 반환
dict_items([(key1, value1{}), (key2, value2{})])
dict_items(
[('-NJFgJWvH5pbTdxdQ1Vs',
{'id': 'ewhain4', 'img_path': '서왕만두군만두.jpg', 'res_name': '미스터서왕만두', 'rev_amount': '적당함', 'rev_etc': ['음식이 맛있음'], 'rev_headcount': '혼자', 'rev_memo': '바삭바삭 맛있어요. 생각보다 양도 많습니다. ', 'rev_menu': '군만두', 'rev_name': '이모조모', 'rev_price': '7000', 'rev_score': '4', 'rev_time': '저녁', 'rev_vegan': 'X'}),
('-NJGPK__Kfz0kON_xKty',
{'id': 'ewhain4', 'img_path': '소룡포.jpg', 'res_name': '미스터서왕만두', 'rev_amount': '적음', 'rev_etc': ['null'], 'rev_headcount': '친구', 'rev_memo': '육즙이 꽉 차있습니다! 맛있어요. ', 'rev_menu': '소룡포', 'rev_name': '이모조모', 'rev_price': '6500', 'rev_score': '5', 'rev_time': '점심', 'rev_vegan': 'X'})])
ㅤdict_items로 값을 반환할 경우, for 순환문으로 각각 item 값에 접근해야 하는 경우가 많았다. 이때, item['rev_score']처럼, 바로 특정 속성에 접근하면 오류가 났다. item[1]['rev_score']과 같이 item[1]의 속성으로, 2차원 배열 접근을 해야 정확한 값을 얻을 수 있다.
ㅤdict_items가 list 형태로 반환되는 값이라면, for item in items는 튜플로 반환되는 값임을 상기하자. item[0]은 key값, item[1]은 하위 딕셔너리 값이다.
ㅤ
ㅤ
1) 템플릿 마련
ㅤ페이지 랜더링에 필요한 html 파일들을 만든다.
ㅤ
2) Flask 컨테이너 생성
ㅤflask 컨테이너에 'templates' 폴더, 'static' 폴더를 생성하고, application.py 파일을 flask 컨테이너 바로 밑에 생성한다.
ㅤ
3) Port 등록 및 python3 application.py 실행

ㅤ프로젝트 속성에서 실행할 url과 Port 번호를 설정 및 등록한다.
ㅤ터미널에서 python3 application.py 명령을 실행한다.
ㅤ
4) application.py 설정
from flask import Flask, render_template
import sys
application = Flask(__name__)
@application.route("/")
def hello():
return render_template("index.html")
if __name__ == "__main__":
application.run(host='0.0.0.0')
ㅤ기본이 되는 코드다. 위를 참고하여 application.py 파일을 설정한다.
라우팅이 필요한 주소는 @application.route("/page_name")으로 정의한다.
주소 밑에 정의되는 함수는 주소에 접속할 때 필요한 템플릿을 라우팅해주는 함수다. 위에서도 언급했지만, 함수 이름은 자유롭게 지을 수 있으나 이름이 겹쳐서는 안 된다.
라우팅은 return render_template("html이름.html", 변수1이름=변수1 ...) 이렇게 가능하다.
ㅤ
5) static 폴더의 파일들을 templates 폴더의 html 파일과 연결
ㅤ기존의 static 파일 저장 경로와 flask에 static 파일 저장 경로가 차이가 날 수 있다.
ㅤ해결 방법은 다음과 같다.
ㅤ- href={ url_for('static'), filename='style.css') }}를 사용하여 경로를 정의
ㅤ- href="/static/style.css"로 정의하여 정적 파일들을 적용하자.
ㅤ
ㅤ단, css와 js 파일이 static 폴더 안에 있을 때 적용이 안 되는 현상도 관찰 가능하므로, 만약 정적 파일이 적용되지 않으면 인라인 태그로 적용하는 것도 대안이 된다.
ㅤ
1) pyrebase 설치
ㅤ터미널에서 pip3 install pyrebase --use-feature=2020-resolver 명령을 실행한다.
ㅤ
2) firebase realtime database 생성
http://firebase.google.com/ 접속 > 프로젝트 만들기 > Realtime Database > 데이터베이스 만들기 > 실시간 데이터베이스 위치 '미국(us-central1)' 설정 > '테스트모드에서 시작' 사용 설정 > 웹 앱에 Firebase 추가 (</> 선택) > 앱 등록 > Firebase SDK 추가의 firebaseConfig의 중괄호({})로 설명된 부분을 복사 > 구름의 flask 컨테이너 바로 밑에 'authentication' 폴더 생성 > firebase_auth.json 파일 생성 > json 파일 안에 복사한 딕셔너리를 그대로 붙여넣고 딕셔너리에 key값을 ""로 감싸줌
ㅤ
3) database.py 생성
import pyrebase
import json
class DBhandler:
def __init__(self):
with open('./authentication/firebase_auth.json') as f:
config=json.load(f)
firebase = pyrebase.initialize_app(config)
self.db = firebase.database()
ㅤ
4) 데이터베이스 구현
ㅤ필요한 데이터베이스 구조를 파악해 db에 접근하는 함수를 database.py 안에 정의한다.
ㅤ
5) application.py에 database.py 모듈 추가
from flask import Flask, render_template, request
from database import DBhandler
import sys
application = Flask(__name__)
DB = DBhandler()
ㅤ
6) static 폴더에 image 폴더 생성
ㅤ따로 저장할 image들을 담아둘 폴더를 생성한다. db에 image 파일의 이름을 저장한다.
ㅤ
ㅤ
맛집 세부 화면을 개발하며 알아간 부분에 대한 해설이다.
ㅤ
ㅤ맛집 세부 화면의 경우 맛집에 대한 정보의 메인이 되는 화면으로, 이용자가 기능들을 바로 확인할 수 있도록 배치했다.
ㅤ
구성요소
1. 맛집 상세 정보
2. 리뷰를 바탕으로 한 해당 맛집 평점
3. 맛집 미니맵
4. 리뷰 미리보기
5. 메뉴 미리보기
ㅤ
ㅤ우선, 일반적으로 가로가 긴 웹사이트의 특성과. 사용자가 정보 확인을 위해 스크롤을 아래까지 길게 내려야 하는 귀찮음을 고려하여 화면을 좌측과 우측(비율 약 6:4)으로 나누어 배치하여 해당 맛집의 정보들이 사용자에게 한 눈에 들어올 수 있도록 설계하였다. 또한 글로 된 정보가 몰려있을 경우 시선이 분산되는 것을 막고자 우측에는 맛집 메뉴의 사진을, 좌측에는 맛집 정보들을 배치하였다.
ㅤ상세히 보면 화면 우측에는 맛집 메뉴 미리보기 창(사진만 표시)과 맛집 메뉴 화면으로 연결된 버튼을, 화면 좌측에는 가게 관련 상세 내용(가격, 주소, 전화번호 등)과 미니맵, 그리고 리뷰 미리보기 창(작은 사이즈로 표시)을 배치하기로 했다.

ㅤ



ㅤ
ㅤ코드를 짜면서 가장 시간이 오래 걸렸던 부분을 꼽아보자면 이렇게 될 것 같다.
ㅤ맛집 세부 화면의 경우, 배경 위에 다시 box를 주어 흰색 내용칸을 만든 다음, 해당 내용칸을 6:4 세로 선을 중심으로 절반으로 나누어 각각의 요소별로 다시 위치시키는 디자인이었는데, 그러다보니 자꾸 내부 요소들이 부모 속성을 따르게 되어 배치 과정에서 온갖 방법을 죄다 활용해야 했다. 가장 골치가 아팠던 것은 중앙정렬이 되지 않는 것이었는데, 해당 문제의 약 85% 정도는 style에 margin: auto;를 주는 것으로 해결되었다. 이걸 적용할 경우 부모 div에 대해 해당 div의 모든 방향의 margin이 일괄 동일하게 적용되어 자동으로 중앙 정렬이 되었다.
ㅤ
ㅤ
box2 - margin auto 예시
box2_8 - margin auto 예시
ㅤ
ㅤ리뷰 창 배치의 경우에는, 사진과 닉네임, 리뷰 내용. 이렇게 세 가지 요소가 있는데, 그냥 배치를 하게 될 경우에는, 전부 정렬이 적용되어서 닉네임 상자와 내용 상자 사이의 간격도
로 밖에는 띄우지 못하고, 게다가 리뷰 길이가 길어질 경우, 사진 아래로까지 내용이 들어가 붙어버리는 경우가 생기거나 하는 바람에, 결국 리뷰 전체 상자(box2_9)와 닉네임상자(nickname2), 내용 상자(content_2)를 각기 따로 만들고, 내용 상자 안에도 태그를 모아두는 상자(haxh)도 따로 만들어야 우리가 원하는 배치가 나왔다.
ㅤ
ㅤ또 문제가 있던 것은 탭(창) 크기를 줄이게 될 경우나 모바일 혹은 태블릿으로 접속하게 될 경우에 요소들이 배치를 유지하지 못하고 찌그러지는 것이었는데, 반응형 웹사이트를 만들기에는 시간이 조금 부족할 것 같아서 그냥 네이버 웹페이지처럼 줄어들다가 어느 순간부터는 고정되도록 적용하기로 했다.
ㅤ다행히 손 볼곳이 그다지 많지는 않았다. 우리 페이지가 대부분 배경 위에 하얀색 영역을 올려두었기에, 그 하얀색 영역에 너비를 주기만 하면 해결되는 문제였다. 이 쪼그라드는 문제 때문에 반응형을 적용해보려고 제법 오랫동안 고민하고 있던 차에 네이버 웹사이트 예시를 팀원분께서 들어주셔서 방법을 찾았다.
ㅤ
ㅤ
(예시 : 네이버 메인 페이지)
ㅤ요모조모 페이지
ㅤ코드
ㅤ
ㅤ이화여자대학교 근방 맛집 소개 웹에서 우리만의 차별화를 주기 위해 우리 조는 여러 기능을 추가로 구성했다. 추가로 구현한 기능은 다음과 같다.
ㅤ
1. 로그인 로그아웃
2. 회원가입
3. My Favorite 식당 기능
4. 예약 기능
5. 랜덤 추천 기능
6. 세분화된 리뷰로 사용자 편의 제공
7. 다양한 카테고리 필터링
8. 식당이름 검색 기능
ㅤ
ㅤ모든 기능은 groom ide 개발환경에서 개발되었고, python, firebase database와 flask를 기본으로 한다. 이 중 랜덤 추천 기능과 예약 기능에 대해서 설명하려고 한다.
ㅤ
ㅤ추천 기능은 처음부터 우리 조가 처음 기획 단계에서부터 차별화로 생각한 기능이다. 우리는 사람들이 보통 식사를 하기 전에 정보를 찾기 위해 식당 소개 웹사이트를 찾을 것이라고 생각하였다. 식사 메뉴로 무엇을 먹을지는 매 끼니마다 많은 사람들에게 고민인데, 식당 소개 웹사이트인만큼 식당을 랜덤으로 추천해주는 기능이 있으면 좋겠다는 생각에 이 기능을 기획하였다.
ㅤ
ㅤ우리가 기획하고 개발한 무작위 추천 기능은 데이터베이스에 저장되어 있는 전체 레스토랑 중, 랜덤으로 하나를 선택해서 화면에 띄워주는 기능이다. category를 filtering하는 바 옆의 ‘무작위 추천’ 버튼을 클릭하여 동작한다.
무작위 추천 기능을 구현을 위해 - (1) 데이터베이스로부터 랜덤으로 하나의 레스토랑을 읽어오는 함수 구현 (2) 무작위로 받아온 데이터를 띄워 줄 html 화면 작성 (3) 해당 화면과 데이터베이스 함수 연결 – 순서로 작업하였다.
ㅤ
@database.py

import random
#무작위 추천
def get_restaurants_byrandom(self):
restaurants = self.db.child("restaurant").get()
target_value=[]
for res in restaurants.each():
value = res.val()
target_value.append(value)
rand = random.choice(target_value)
return rand #restaurant 랜덤으로 하나 받아온다.
ㅤdatabase로부터 무작위로 식당을 받아오는 함수를 database.py 내에 get_restuarnats_byrandom(self) 으로 구현했다.
먼저, database 내의 식당 정보가 저장되어 있는 restaurant table로부터 전체의 정보를 받아온다. 그런데, firebase는 정보를 부모자식 관계인 Node 형태의 방식으로 저장한다. 일종의 Dictionary로 키와 값의 구조이기 때문에 firebase로부터 받아온 정보를 바로 활용하는 것이 어려웠다. 그래서 firebase로부터 받아온 restaurant table 전체의 정보를 for 문을 통해 하나씩 읽으면서 키, 즉 res.val()을 읽어 생성한 target_value라는 빈 리스트 안에 append로 추가한다.
for loop이 끝나고 나면 받아온 식당 전체의 정보 중 키 값만이 target_value라는 list안에 저장된다. 데이터를 받아오고 사용할 수 있게 가공하는 것이 가장 어려웠고 이 다음부터는 python의 랜덤 모듈을 활용하면 되어 매우 간단했다. python의 랜덤 모듈을 사용하려면 랜덤 모듈을 import 해야 한다. 랜덤 모듈이 제공하는 choice() 함수를 활용해서 target_value의 list에서 랜덤하게 하나의 원소를 선택했다. 이후 선택한 해당 원소를 return하면 무작위로 식당 하나가 선택되어 식당 키 값이 반환되었다.
ㅤ
@random_res.html
{% extends "index.html" %}
{% block section %}
<p class="ranking">
무작위 추천 결과
</p>
<br>
<div class="contents_random" >
<p style="margin-left:5px;">
랜덤 추천 결과입니다!
</p>
<article>
<a href="/view_detail/{{rand.res_name}}/">
<img src="/static/image/{{rand.img_path}}" style="width: 260px; height: 270px; object-fit: cover;"></a>
<p>이름: {{rand.res_name}}</p>
</article>
</div>
{% endblock section %}
ㅤ데이터를 읽어들일 html 화면은 기존의 index.html에서 구현해두었던 것을 extends로 활용해서 구현하였다. 앞서 database.py에서 랜덤으로 가져온 식당 정보를 받아 이 html 화면에 보여주면 된다. 식당 정보는 키 값으로 들어왔기 떄문에 키 값을 활용하면 해당 식당 키 아래에 저장되어 있는 다른 정보에도 접근할 수 있다. 이 정보 중 식당의 이름 {{rand.res_name}}과 이미지 {{rand.img_path}}를 읽어 html에 출력하였다.
출력된 하나의 식당 이미지를 클릭하면 바로 해당 식당의 상세페이지(detail.html)로 연결할 수 있도록 href 링크에 {{rand.res_name}}을 활용했다.
ㅤ
@application.py
#random 추천 화면 연결
@application.route("/random_res")
def random_res():
rand=DB.get_restaurants_byrandom()
return render_template("random_res.html", rand=rand)
ㅤ이제 데이터베이스로부터 하나의 식당을 랜덤으로 가져오는 함수와 읽은 정보를 보여줄 html 화면도 구현했으니 둘을 연결하기만 하면 된다. 만든 random_res.html에 DB에 작성한 함수 get_restauarants_byrandom()를 연결했다.
ㅤ이렇게 구현한 무작위 추천 기능 실행 결과는 다음과 같다.
ㅤ
ㅤ이렇게 간단하게 수업 중에 배웠던 데이터베이스 읽는 방법과 html과 데이터베이스를 연결하는 방법을 활용해서 random 추천 기능을 구현하였다. 기본 베이스 언어를 파이썬을 사용하고 있어서 수월하게 구현할 수 있었다.
ㅤ
ㅤ예약 기능은 html에서 배웠던 가장 기본적인 지식을 활용했다. 전화 연결, 사이트 연결은 이미 수업시간에 배웠기 때문에 간단하게 구현할 수 있을 것이라고 생각했다. 그런데 수업시간과는 달리 database로부터 data를 받아와서 링크에 연결하는 것이라, 이 부분이 생각 외로 구현에 시간이 오래 걸렸다.
ㅤ
@detail.html
{% if (data['res_tel'] != '') and (data['res_tel'] != 'X') and (data['res_tel']
!= 'x') and (data['res_tel'] != '-') %} {{data.res_tel}}
<button onclick="document.location.href='tel:<%={{data.res_tel}}%>'">
전화 연결
</button>
<br />
{% endif %} {% if (data['res_site'] != '') and (data['res_site'] != 'X') and
(data['res_site'] != 'x') and (data['res_site'] != '-') %} {{data.res_site}}
<button onclick="window.open('{{data.res_site}}')">사이트 연결</button>
<br />
ㅤ식당 상세정보 화면은 restaurant table에 만약 식당 전화번호와 주소가 저장되어 있는 경우, 해당 전화번호와 주소 데이터를 받아와서 detail.html 화면에 가져온다. {% if %}와 {% endif %} 조건문을 활용했다. 이 가져오는 데이터를 활용해서 예약 기능을 구현하기로 했다.
ㅤ수업 시간에 html에서 전화 연결은 을 활용, 사이트 연결은 <a href=’연결 주소’>내용를 활용해서 구현한다고 배웠다. 배운 내용을 적용해 받아온 restuarant table에서 받아온 식당 전화번호 정보 {{data.res_tel}}과 {{data.res_site}}를 링크로 바로 활용하니, 이를 인식하지 못해서, 링크가 걸리지 않았다.
ㅤDB로부터 정보는 잘 담겨왔으나 값이 넘어가지 않는 것을 해결하기 위해서 정보 앞 뒤에 %를 넣는 href='tel:<%=str%>'로 시도해보았으나, 이 역시도 문제를 해결해주지는 않았다. DB로부터 정보를 받아와 클릭 이벤트로 활용하는 방법은 검색해도 정보가 별로 없어 고민이 많았다.
ㅤ데이터베이스로부터 읽은 정보를 직접 활용했을 때 링크가 걸리지 않았기 때문에, 고민 끝에 받아온 {{data.res_site}} 데이터베이스 정보를 바로 링크로 활용하지 않고, 별도의 버튼으로 이용해 href 구현을 했다. 전화번호와 사이트 내용을 바로 클릭하지 않고 별도 버튼을 사용했더니 버튼 클릭시에 링크가 걸려서 잘 동작했다.
ㅤ다음은 예약 기능 구현의 결과이다. 전화번호 옆의 전화연결 버튼을 클릭하면 모바일 환경에서는 바로 전화로 연결된다. 또한 사이트 주소 옆의 사이트 연결 버튼을 클릭하면 해당 사이트로 연결된다.