SSR과 JWT을 학습하면서, 이를 JungleGYM의 기능에 적용해 보고자 했다.
우선, 한 가지 의문이 들었다.
사용자 맞춤형 웹페이지 제작
검색 필터에 → 사용자가 신청한 카드들이 보여질 수 있도록 : “신청한 내역”
(클릭했을 때 뜨는) 상세 카드 페이지에
→ 이미 신청자는 “취소하기” 버튼을
→ 신청 안 한 사람은 “신청하기” 버튼을
을 구현해야겠다!고 당차게 시작했다.
하지만, 예상보다 쉽지 않았던 트러블 슈팅이 생겨 많이 헤매었다.
JinJa2 - main.html 렌더링 문제 발생
main.html
을 상속 받는 listing.html과 finding.html이 있음 (두 소스 코드는 동일함) {% content block %}
{% endblock %}
사이에 listing.html
은 렌더링 되는 반면, finding.html
은 렌더링이 되지 않음finding.html
이 보여지지 않는 문제.조금 자세하게 풀어 쓰면, 아래와 같다.
DB에 저장된 모든 카드들을 보여주는 것
(조건을 가지고) DB에 저장된 카드들 중 조건을 만족하는 것만 보여주는 것
1.상단의 헤더/검색 필터까지 그리고 푸터를 main.html에서 구현하여 하나의 base.html로서의 기능을 하도록 함
을 뿌려주도록 하기 위해
메인에서 {% block content %} {% endblock %} 을 지정하여
이 부분에 listing.html과 finding.html을 렌더링 하도록 시도함
(물론, listing.html과 finding.html은 main.html을 상속 받음)
<!-- main.html -->
...
<div id="card-list" class="card-columns">
{% block content %}
{% endblock %}
</div>
...
listing.html과 finding.html은 양식이 동일하지만, 라우트를 복잡하게 하고 싶지 않아 별도로 만듦
{% extends 'main.html' %}
{% block content %}
{% for memo in memos %}
<div class="container row-cols-md-3" style="display: flexbox; margin-top: 50px;">
<div class="wrap" id="card-id-{{ memo.card_id }}">
<a onclick="modal_open('{{memo.card_id}}')">
<article class="message is-success" id="mainform-{{ memo.card_id }}">
<div class="message-header">
<p>{{ memo.creator }}</p>
</div>
<div class="message-body">
{{ memo.text }}
</div>
</article>
</a>
</div>
<div class="modal" id="subform-{{ memo.card_id }}">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">게시글 정보</p>
</header>
<section class="modal-card-body">
<p>레벨 : {{ memo.level }}</p>
<p>종목 : {{ memo.sport }}</p>
<p>시간 : {{ memo.time }}</p>
<p>장소 : {{ memo.spot }}</p>
<p>성별 : {{ memo.gender }}</p>
<p>총 {{ memo.cnt }}명 신청</p>
<p>{{ memo.m1 }} {{ memo.m2 }} {{ memo.m3 }}</p>
</section>
<footer class="modal-card-foot">
<button class="button is-success" onclick="join('{{ memo.card_id }}')">참가하기</button>
<button class="button" onclick="modal_close('{{ memo.card_id }}')">닫기</button>
</footer>;
</div>
</div>
{% endfor %}
{% endblock %}
3. listing.html은 렌더링 성공함
@app.route('/main', methods=['GET'])
def main():
memos = list(db.memos.find({}, {'_id' : False}))
return render_template('listing.html', memos=memos)
원리는 (진자2)
4. finding.html은 렌더링 실패함
@app.route('/main/finding', methods=['POST'])
def finding():
level_find = request.form['level']
sport_find = request.form['sport']
time_find = request.form['time']
spot_find = request.form['spot']
gender_find = request.form['gender']
opt = {'level': level_find, 'sport': sport_find, 'time': time_find, 'spot': spot_find, 'gender': gender_find}
memos = list(db.memos.find(opt, {'_id' : False}))
return render_template('finding.html', memos=memos)
원리는 (진자2로 시도)
방법 1. POST 방식이 아닌 GET 방식으로 구현해 봄
**let queryString = $.param(result); // result 객체를 쿼리스트링으로 변환**
$.ajax({
**type: "GET", // GET 방식으로 변경
url: "/main/finding?" + queryString, // 쿼리스트링을 URL에 추가**
success: function(response) {
if (response['result'] == 'success'){
alert("찾았습니다!")
} else {
alert("찾기 실패!")
}
}
})
@app.route('/main/finding', methods=['GET'])
def finding():
# 1) 클라이언트로부터 데이터를 받는다.
**level_find = request.args.get('level')**
sport_find = request.args.get('sport')
time_find = request.args.get('time')
spot_find = request.args.get('spot')
gender_find = request.args.get('gender')
# DB에 있는 정보들 다 불러오기
memos = list(db.memos.find({'level': level_find, 'sport': sport_find, 'time': time_find, 'spot': spot_find, 'gender': gender_find}, {'_id' : False}))
# 화면을 띄울 때, 메모 카드들을 불러옴과 동시에 id_list를 셋팅한다.
# 새로고침 하더라도 id_list에 card_id들이 중복되어 들어가지 않도록 한다.
**return jsonify({'result': 'success', 'memos':memos})**
⇒ render_template()에 실패
방법 2. redirect url_for로 변수를 다른 url에 전달한 후, 그 url에서 다시 render_template을 하는 방향
⇒ 어쨌든 안 됨
⇒ josn 형식의 response를 리턴해야 하는데, 즉, render_template() 사용이 불가능하다고 판단함
⇒ 이렇게 하든, 처음부터 render_template()을 하든 render_template() 함수 실행해서 계속 막힘
그래서 시도
검색하기 버튼 누를 때, 브라우저 개발자 도구에 들어가 네트워크를 확인하면 preview와 response는 데이터를 보여줌. 다만, 실제 화면으로 보여지지 않을 뿐.
그래서 결국
백에서 데이터를 프론트로 넘겨, 프론트에서 html을 구성해 일일히 append 하는 방식으로 구현했다.
즉, SSR이 아닌 CSR (클라이언트 사이드 렌더링) 방식으로 뿌릴 수밖에 없었다.
즐거웠다.
체력적으로 많이 힘들었지만, 정말 즐거웠다.
개인적으로 좋았던 부분은, 노션 워크스페이스를 통한 개발 주기 관리를 한 측면이었다.
짧은 기간의 프로젝트여서 각각의 분업과 기록이 많이 남은 편은 아니었으나, 편리하고 빠르게 또 가시적으로 개발 타임라인을 관리할 수 있어서 유용했다.
또한 아쉬웠던 부분은, (최종 발표 이후에 받은 피드백이)
"오래 사용할 것 같지 않은 서비스"였던 것이다.
즉, UX UI가 부족한 (사용자를 고려하지 않은) 서비스였다.
돌이켜 보면, 기능을 여러 개 욕심 내다 보니 UX와 UI를 더 탄탄하게 구성하지 못 했던 것 같다.
따라서, 다음에는 한 가지 기능이더라도 탄탄하게 그리고 뻔쩍하게! 구현하는 데 집중해야겠다.