from django.shortcuts import render, redirect
from board.models import Post
from django.core.paginator import Paginator
from django.contrib.auth.decorators import login_required
from django.core.files.storage import default_storage
import uuid
def board(request):
# 게시글 리스트
if request.method == "GET":
page = request.GET.get('page', 1)
search_text = request.GET.get('search_text', "")
post_set = Post.objects.filter(
title__icontains=search_text
).order_by('-id')
paginator = Paginator(post_set, 2)
post_set = paginator.get_page(page)
context = {
"post_set":post_set,
"search_text":search_text,
}
return render(request, 'page/index.html', context=context)
@login_required(login_url="signin")
def post_write(request):
if request.method=="GET":
return render(request, "page/post_write.html")
if request.method=="POST":
title = request.POST["title"]
content = request.POST["content"]
img = request.FILES.get('img', None)
img_url = ""
if img:
img_name = uuid.uuid4()
ext = img.name.split('.')[-1]
default_storage.save(f"{img_name}.{ext}", img)
img_url = f"{img_name}.{ext}"
#이미지 저장!
Post(
img_url=img_url,
user=request.user,
title=title,
content=content,
).save()
return redirect('board')
def post_detail(request, post_id):
if request.method=="GET":
post = Post.objects.get(id=post_id)
context = {
"post":post
}
return render(request, 'page/post_detail.html', context=context)
# anonymous>urls.py
from django.urls import path
from board.views import board, post_write, post_detail
from user.views import signin, signup, sign_out
urlpatterns = [
path("", board, name="board"),
path("user/signin", signin, name = "signin"),
path("user/signup", signup, name="signup"),
path("user/signout", sign_out, name="signout"),
path("post/write", post_write, name="post_write"),
path("post/<int:post_id>", post_detail, name="post_detail"),
]
<!-- templates/page/post_detail.html -->
{% extends "common/base.html" %}
{% block content %}
여기는 상세 페이지 입니다.
{% endblock %}
def post_detail(request, post_id):
if request.method=="GET":
print(post_id)
post = Post.objects.get(id=post_id)
context = {
"post":post
}
return render(request, 'page/post_detail.html', context=context)
<!-- post_detail.html -->
s
{% extends "common/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="row">
<div class="col">
<p>제목</p>
<p>닉네임</p>
<p>내용</p>
<img src="">
</div>
</div>
</div>
</div>
{% endblock %}
댓글 추가
<!-- post_detail.html -->
...
</div>
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<p>날짜</p>
<p>내용</p>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
... <!-- 댓글 영역 앞에 -->
<div class="row">
<div class="col">
<div class="card">
<div class="card-body">
<textarea class="form-control"></textarea>
<a
class="text-decoration-none fs-5 my-2" id="write_btn"
style="font-weight: bold;color: black;">글 작성</a>
</div>
</div>
</div>
</div>
...
... <!-- 댓글 영역 앞에 -->
<div class="row text-end">
<div class="col">
<div class="card">
<div class="card-body">
<textarea
placeholder="한번 작성한 글은 삭제할 수 없습니다. 매너를 지켜주세요."
class="form-control"
style="resize: none;height: 100px;"></textarea>
<a
class="text-decoration-none fs-5 my-2" id="write_btn"
style="font-weight: bold;color: black;">작성</a>
</div>
</div>
</div>
</div>
...
게시글 상세 내용에 해당하는 디자인 수정
{% extends "common/base.html" %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="row">
<div class="col">
<p class="card-title"
style="font-weight: bold;"
>제목</p>
<p class="card-subtitle"
style="font-size: 12px;"
>닉네임 <small>2000-01-01 12:12:12</small></p>
<p class="card-text"
style="margin-top: 10px;"
>내용</p>
<img src="">
</div>
</div>
<div class="row text-end" style="margin-top: 40px;">
<div class="col">
<div class="card">
<div class="card-body">
<textarea
placeholder="한번 작성한 글은 삭제할 수 없습니다. 매너를 지켜주세요."
class="form-control"
style="resize: none;height: 100px;"></textarea>
<a
class="text-decoration-none fs-5 my-2" id="write_btn"
style="font-weight: bold;color: black;">작성</a>
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="card">
<div class="card-body">
<p>날짜</p>
<p>내용</p>
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="card">
<div class="card-body">
<p>날짜</p>
<p>내용</p>
</div>
</div>
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="card">
<div class="card-body">
<p>날짜</p>
<p>내용</p>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
다음 강의에서 개발할 댓글 기능을 위해 form 태그로 감싸기
...
<form>
<textarea
placeholder="한번 작성한 글은 삭제할 수 없습니다. 매너를 지켜주세요."
class="form-control"
style="resize: none;height: 100px;"></textarea>
<a
class="text-decoration-none fs-5 my-2"
id="write_btn"
style="font-weight: bold;color: black;">댓글 작성</a>
</form>
...
<!-- index.html -->
...
<div class="row">
{% for post in post_set %}
<div class="col-6 p-2">
<div class="card">
<a class="text-decoration-none" style="color: black;" href="{% url 'post_detail' post.id %}">
<div class="card-body">
<h5>{{ post.title }}</h5>
<p>{{ post.content }}</p>
<p>
...
오늘은 다시 프론트엔드로 넘어와 게시판 상세 페이지와 댓글을 만들었다.
먼저, 게시판 상세 페이지는 post_detail.html을 작성하고, board의 views.py에 post_detail 함수 선언 및 anonymous의 urls.py에서 경로 지정을 해주어 구현했다.
그 다음, 상세 페이지 내에 댓글도 함께 표시될 수 있도록 상세페이지 카드에 댓글 카드를 내부에 넣는 작업을 해줬고, textarea를 만들어 댓글을 쓰는 공간도 만들었다.
댓글 기능도 크게 보면 글쓰기 기능과 똑같을 수밖에 없기 때문에 디자인과 태그 등을 거의 동일하게 사용해 만들어 효율을 높였다.
디자인 디테일도 bootstrap의 card title, card subtitle, card-text로 클래스를 지정해주고 각 스타일을 적용해서 게시판의 글과 유사한 형태로 만들기 위해 수정했다.
또한, 추가로 글을 작성한 시간까지 small 태그를 이용해 작성해주었다.
그 다음, 실제 게시글을 클릭했을 때에도 /post/{id} 상세 페이지로 이동할 수 있도록 처리해줬다.
이렇게 프론트의 기능 구현에 대해서는 모든 강의를 듣게 되었다. 백엔드가 아직 한 단계 남아있기도 하고, 아주 기본적인 기능만을 빠르게 넣어서 만들었기 때문에 아직 만족스러운 기능을 갖추고 있지는 않기 때문에 조금 고민이 되지만, 이 강의를 통해서 bootstrap의 사용법을 배울 수 있었던 덕분에 springboot를 이용한 간단한 투두 리스트를 만드는 프로젝트에서 프론트에 활용할 수 있었다.
배운 기술을 그대로 쓰는 것도 좋지만, 이렇게 자신의 주력 프레임워크에 활용해 사용하는 방법도 학습에 좋은 영향을 끼친다고 생각한다.
그렇기 때문에 새로운 기술 스택을 쌓을 때, 내가 주력으로 사용하지 않을 것이고 주력 스택만 하기에도 벅차다고 생각하기보단 어떻게 적용핢 수 있을 것인지에 대해서 더 고민하는 시간을 가져보려고 한다.
앞으로도 남은 일주일 화이팅 👏