이제 요약된 정보를 조회를 해보자.
요약된 정보를 조회하기 위해서는 게시글 디테일 페이지에 접근할 때, 그 내용까지 같이 전달이 필요하다.
디테일페이지에 요약 정보도 같이 조회해서 가져오자.
chatgpt.services.py
def get_latest_post_summary_by_post_id(post_id: int) -> PostSummary:
return PostSummary.objects.filter(post_id=post_id).order_by('-created_at').first()
board.views.py
post_summary 를 조회한다.
def post_detail(request, board_url, pk):
qs = Post.objects.active().filter(
board__url=board_url
).select_related(
'board'
).order_by(
'-id'
)
prev_post = qs.filter(id__lt=pk).first()
next_post = qs.filter(id__gt=pk).order_by('id').first()
post = get_object_or_404(qs, board__url=board_url, pk=pk)
post_summary = get_latest_post_summary_by_post_id(post.id)
if request.user.is_authenticated:
like_check = Like.objects.filter(author=request.user, post=post).exists()
else:
like_check = False
context = {
'like_check': like_check,
'qs': qs,
'post': post,
'post_summary': post_summary,
'prev_post': prev_post,
'next_post': next_post,
}
return render(request, 'board/post.html', context)
board/post.html
<style>
p {
word-break: break-word;
}
.other_post {
text-decoration: auto;
}
.reply_call:hover {
cursor: pointer;
}
.reply_del:hover {
cursor: pointer;
}
.rereply_del:hover {
cursor: pointer;
}
.new-list-title {
font-size: small;
}
.progress-container {
left: 0;
width: 100%;
height: 0.4em;
margin-bottom: 0px;
position: fixed;
top: 0px;
overflow: hidden;
background-color: white;
content: "";
display: table;
table-layout: fixed;
z-index: 1000;
}
.progress-bar {
width: 0%;
float: left;
height: 100%;
z-index: 99;
max-width: 100%;
background: rgb(132, 94, 194);
background: linear-gradient(90deg, rgba(132, 94, 194, 1) 5%, rgba(44, 115, 210, 1) 22%, rgba(0, 129, 207, 1) 42%, rgba(0, 137, 186, 1) 62%, rgba(0, 142, 155, 1) 85%, rgba(0, 143, 122, 1) 100%);
-webkit-transition: width .4s ease;
-o-transition: width .4s ease;
transition: width .4s ease;
}
.summary-card {
width: 100%;
max-width: 1200px;
margin: 20px 0 20px 0;
padding: 20px;
border: 1px solid #D2D2D2;
border-radius: 8px;
box-sizing: border-box;
}
.summary-card-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.summary-card-header img {
width: 40px;
height: 40px;
margin-right: 10px;
}
.summary-card-header h3 {
margin: 0;
font-size: 1.2em;
}
.summary-card-content {
font-size: 1em;
color: #333;
line-height: 1.6;
}
.loading-spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-top: 4px solid #3498db; /* 파란색 */
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
display: inline-block;
margin-right: 10px; /* 텍스트와 스피너 간격 */
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
{% if post_summary %}
<div class="summary-card">
<div class="summary-card-header">
<h3>ChatGPT 요약</h3>
</div>
{% if post_summary.status == 'DONE' %}
<div class="summary-card-content" id="summary-card-content-done">
{{ post_summary.body }}
</div>
{% elif post_summary.status == 'PROCESSING' %}
<div class="summary-card-content" id="summary-card-content-processing">
<div class="loading-spinner"></div>
요약중입니다. 잠시만 기다려주세요.
</div>
{% elif post_summary.status == 'FAIL' %}
<div class="summary-card-content" id="summary-card-content-fail">
요약에 실패했습니다... :(
</div>
{% endif %}
</div>
{% endif %}
간단하게 이렇게 작성했다.
우선 신규 API 를 생성합니다.
chatgpt.views.py
from django.http import JsonResponse
from chatgpt.services import get_latest_post_summary_by_post_id
def get_summary_by_post_id(request, post_id: int):
if post_id is None:
return JsonResponse({'error': 'post_id is required'}, status=400)
post_summary = get_latest_post_summary_by_post_id(post_id)
context = {
'status': post_summary.status if post_summary else None,
'summary': post_summary.body if post_summary else None,
}
return JsonResponse(context, status=200)
PROCESSING 인 부분에 추가 내용을 적을 것입니다. 여기서는 계속 polling 하는 javascript 코드를 작성할 것입니다.
{% if post_summary %}
<script>
document.addEventListener('DOMContentLoaded', function () {
// "summary-card-content-processing" 요소가 있는지 확인
let processingElement = document.getElementById('summary-card-content-processing');
// 요소가 존재할 경우에만 폴링 시작
if (processingElement) {
let pollingInterval = setInterval(function () {
// API를 호출 (여기선 fetch를 예로 사용)
fetch("{% url 'chatgpt:get_summary_by_post_id' post_id=post.id %}") // API 엔드포인트로 변경
.then(response => response.json())
.then(data => {
if (data.status !== 'PROCESSING') {
let div_id = 'summary-card-content-' + data.status.toLowerCase()
document.getElementById('summary-card-content-processing').id = div_id;
document.getElementById(div_id).innerHTML = data.summary || '';
clearInterval(pollingInterval);
}
})
.catch(error => {
console.error('Error fetching the API:', error);
// 에러 발생 시에도 폴링 중단할 수 있음
clearInterval(pollingInterval);
});
}, 5000); // 5000ms = 5초
}
});
</script>
{% endif %}
요약 로딩
요약 존재
요약 실패