[1] 개요
1. URL
1) 리스트 화면 : HOST/board
2) 등록 화면 : HOST/board/iv
3) 상세 화면 : HOST/board/g/<int:pk>
4) 수정 화면 : HOST/board/uv/<int:pk>
2. 구성
1) 리스트 화면
- 게시글을 한 페이지에 5개씩 출력
- 로그인 상태에서만 등록 버튼 출력
2) 등록 화면
- 제목, 내용만 입력하여 등록
3) 상세 화면
- 제목, 글번호, 글쓴이, 작성일, 내용을 출력
- 로그인된 ID로 작성한 글만 수정/삭제 버튼 출력
4) 수정 화면
- 기존의 제목, 내용이 출력된 상태에서 수정
[2] CODE
1. 리스트 화면
1) HTML
- ajax로 데이터 5개 가져와서 리스트로 출력
- board table에 있는 레코드 수에 따라 페이지 번호 버튼도 생성
/templates/boardlist.html
<div class="board_wrap">
<div class="board_title">
<strong>고객 상담 </strong>
<p>답변을 빠르고 정확하게 안내해드립니다.</p>
</div>
<div class="board_list_wrap">
<div id="board_list" class="board_list">
<!--ajax로 리스트 뿌리는 곳-->
</div>
<div class="board_page">
<a href="#" class="bt first" onclick="getFirst();"><<</a>
<a href="#" class="bt prev" onclick="getPrevious(idx, page);"><</a>
<div id="page_list" style="display:inline;">
<!--Page 번호 뿌려지는 곳-->
</div>
<a href="#" class="bt next" onclick="getNext(idx, page);">></a>
<a href="#" class="bt last" onclick="getLast();">>></a>
</div>
{% if request.session.sessionid != none %}
<div class="bt_wrap">
<a href="/board/iv" class="on">등록</a>
<!--<a href="#">수정</a>-->
</div>
{% endif %}
</div>
</div>
2) CSS
- /static/css/style.css + media.css
3) JavaScript
- 페이지 생성을 위한 변수와 함수들로 구성
① getData(i, p) : 테이블의 인덱스 번호와 현재 페이지를 받아서 ajax를 통해 데이터를 가져오는 함수
② display(data) : 가져온 데이터를 출력해 주는 함수
③ getFirst, getPrevious, getNext, getLast : 첫페이지, 이전페이지, 다음페이지, 마지막 페이지 출력을 위한 함수
④ getPage() : 각 페이지 버튼 클릭 시 해당 페이지로 이동
⑤ makePageButton() : 테이블 전체 데이터 수에 맞게 페이지 버튼 생성
⑥ currentPage() : 페이지 버튼에 현재 페이지 표시
/templates/boardlist.html -> <script>태그
var idx = 0; // 데이터 뿌리는 시작 인덱스(0이면 제일 최신 데이터) (변동)
var page = 1; // 화면 뜨면 1페이지로 setting (변동)
var cnt = {{count}}; // 게시판 데이터 갯수 (고정)
if (cnt % 5 == 0){
var pages = parseInt(cnt/5); // 게시판 페이지 수 계산 (고정)
var remain = 5; // 게시판 마지막 페이지 데이터 갯수 (고정)
} else {
var pages = parseInt(cnt/5) + 1;
var remain = cnt % 5;
}
// 데이터 출력
function display(data){
results = ''
results = '<div class="top">';
results += '<div class="num">번호</div>';
results += '<div class="title">제목</div>';
results += '<div class="writer">글쓴이</div>';
results += '<div class="date">작성일</div>';
results += '</div>';
$(data).each(function(index, item){
var result = '';
result += '<div>';
result += '<div class="num">' + item.id + '</div>';
result += '<div class="title"><a href="/board/g/'+item.id+'">' + item.title + '</a></div>';
result += '<div class="writer">' + item.cust_id + '</div>';
result += '<div class="date">' + item.regdate + '</div>';
result += '</div>'
results += result;
$('#board_list').html(results);
});
}
// 해당하는 index와 page 정보로 데이터 가져오기
function getData(i, p){
if (p == pages){
var getcnt = remain;
} else {
var getcnt = 5
}
// alert('page'+page+' / '+'pages'+pages);
$.ajax({
url: '/board/listview/'+i+'/'+getcnt,
success: function(data){
display(data);
}
});
}
// 첫 페이지
function getFirst(){
if (idx == 0){
alert('첫 페이지입니다.');
return;
}
idx = 0;
page = 1;
getData(idx, page);
currentPage();
}
// 이전 페이지
function getPrevious(i, p){
if (i == 0){
alert('첫 페이지입니다.');
return;
}
idx = i - 5;
page = p - 1;
getData(idx, page);
currentPage();
}
// 다음 페이지
function getNext(i, p){
if (p == pages){
alert('마지막 페이지입니다.');
return;
}
idx = i + 5;
page = p + 1;
getData(idx, page);
currentPage();
}
// 마지막 페이지
function getLast(){
if (page == pages){
alert('마지막 페이지입니다.');
return;
}
idx = pages * 5 - 5 // 마지막 페이지 시작 인덱스 계산
page = pages;
getData(idx, page);
currentPage();
}
// 각 페이지 버튼 클릭 시 해당 페이지로 이동
function getPage(i){
idx = i * 5 - 5 // 해당페이지 시작 인덱스 계산
page = i;
getData(idx, page);
currentPage();
}
// 페이지 수에 맞게 페이지 버튼 생성
function makePageButton(){
results = '';
for (var i=1; i<pages+1; i++){
result = '';
result += '<a href="#" id="num' + i + '" onclick="getPage(' + i + ');" class="num">'
result += i;
result += '</a>';
results += result;
}
$('#page_list').html(results);
}
// 페이지 버튼에 현재 페이지 표시
function currentPage(){
for (var i=1; i<pages+1; i++){
var id = '#num' + i;
if (i == page){
$(id).addClass('on');
} else {
$(id).removeClass('on')
}
}
}
$(document).ready(function(){
getData(idx, page);
makePageButton();
currentPage();
});
4) Application
① board() : boardlist.html 을 테이블 레코드 수와 함께 보내주는 함수
② listview(idx, getcnt)
- ajax로 요청 받는 함수
- 테이블 인덱스와 가져올 데이터 수를 받아서 테이블에서 해당 레코드들을 불러옴
- 불러온 데이터를 json데이터로 변환하여 ajax에게 보내줌
/web/views_board.py -> class BoardView(View)
@request_mapping("/", method="get")
def board(self, request):
count = Board.objects.all().count()
context = {
'count': count
}
return render(request, 'boardlist.html', context)
@request_mapping("/listview/<int:idx>/<int:getcnt>", method="get")
def listview(self, request, idx, getcnt):
objs = Board.objects.all().order_by('-id')[idx:idx+getcnt]
data = []
for obj in objs:
datum = dict()
datum['id'] = str(obj.id)
datum['title'] = str(obj.title)
datum['cust_id'] = str(obj.cust.id)
datum['regdate'] = str(obj.regdate)
data.append(datum)
return HttpResponse(json.dumps(data), content_type='application/json')
2. 등록 화면
1) HTML
- 모든 input 태그에 required 속성 부여
- 등록 버튼 클릭 시 /board/i 를 get 방식으로 요청
- 취소 버튼 클릭 시 해당 clickCancel() 함수 실행(JavaScript에 존재)
/templates/boardreg.html
<div class="board_wrap">
<div class="board_title">
<strong>질문 등록</strong>
<p>답변을 빠르고 정확하게 안내해드립니다.</p>
</div>
<form action="/board/i" method="get">
<div class="board_write_wrap">
<div class="board_write">
<div class="title">
<dl>
<dt>제목</dt>
<dd><input type="text" placeholder="제목 입력" name="title" required></dd>
</dl>
</div>
<div class="cont">
<textarea placeholder="내용 입력" name="content" required></textarea>
</div>
</div>
<div class="bt_wrap">
<a href="#" class="on"><button type="submit">등록</button></a>
<a href="#" onclick="clickCancel();">취소</a>
</div>
</div>
</form>
</div>
2) CSS
- /static/css/style.css + media.css
3) JavaScript
- 취소 버튼 클릭 시 확인 창을 띄우기 위한 함수
- 확인 누르면
/templates/boardreg.html -> <script> 태그
function clickCancel(){
c = confirm('[주의]\n작성한 내용이 저장되지 않았습니다.\n정말 질문 등록을 취소하시겠습니까?');
if(c == true){
location.href = '/board';
}
}
4) Application
① insertview() : 등록 화면 출력
② insert() : form으로부터 get 방식으로 받은 제목과 내용을 DB에 저장 후, 리스트 화면으로 이동
/web/views_board.py -> class BoardView(View)
@request_mapping("/iv", method="get")
def insertview(self, request):
return render(request, 'boardreg.html')
@request_mapping("/i", method="get")
def insert(self, request):
title = request.GET['title']
content = request.GET['content']
objs = Board(cust_id=request.session['sessionid'], title=title, content=content)
objs.save()
return redirect('/board')
3. 상세 화면
1) HTML
- Application에서 받은 데이터를 context로 받아서 항목별로 출력
- 목록 버튼 클릭 시 리스트 화면으로 이동
- 수정/삭제 버튼은 로그인 상태에서만 보이도록 if문 안에 작성
- 수정 버튼 클릭 시 해당 글 ID를 URL로 가지고 수정 화면으로 이동
- 삭제 버튼 클릭 시, 확인창을 띄우고, 삭제하기 위해 clickDelete() 함수 실행(JavaScript에서)
/templates/boardget.html
<div class="board_wrap">
<div class="board_title">
<strong>고객 문의</strong>
<p>빠르고 신속하게 답변 드리겠습니다.</p>
</div>
<div class="board_view_wrap">
<div class="board_view">
<div class="title">
{{objs.title}}
</div>
<div class="info">
<dl>
<dt>번호</dt>
<dd>{{objs.id}}</dd>
</dl>
<dl>
<dt>글쓴이</dt>
<dd>{{objs.cust.id}}</dd>
</dl>
<dl>
<dt>작성일</dt>
<dd>{{objs.regdate}}</dd>
</dl>
</div>
<div class="cont">
{{objs.content}}
</div>
</div>
<div class="bt_wrap">
<a href="/board" class="on">목록</a>
{% if request.session.sessionid == objs.cust.id%}
<a href="/board/uv/{{objs.id}}">수정</a>
<a href="#" onclick="clickDelete();">삭제</a>
{% endif %}
</div>
</div>
</div>
2) CSS
- /static/css/style.css + media.css
3) JavaScript
- 삭제 버튼 클릭 시, 확인창 띄우고, 확인 시에는 해당 글의 ID를 URL로 가지고 Application으로 이동하여 삭제
/templates/boardget.html -> <script>태그
function clickDelete(){
c = confirm('[주의]\n삭제 후에는 복구할 수 없습니다.\n정말로 삭제하시겠습니까?');
if(c == true){
location.href = '/board/d/' + {{objs.id}}
}
}
4) Application
① get(pk) : URL로 받은 글 ID(pk)로 테이블에서 객체를 가져와서 상세 화면(boardget.html)에 context로 보내줌
② delete(pk)
- URL로 받은 글 ID(pk)와 sessionid를 함께 이용하여 객체를 가져와서 삭제 실행
- sessionid까지 이용하는 것은 아무나 URL을 통해 삭제 실행하지 못하도록 하기 위함
- 삭제에 성공하거나, 임의로 URL로 삭제 시도 시, 리스트 화면으로 이동
/web/views_board.py -> class BoardView(View)
@request_mapping("/g/<int:pk>", method="get")
def get(self, request, pk):
objs = Board.objects.get(id=pk)
context = {
'objs': objs
}
return render(request, 'boardget.html', context)
# ============================================================================================
@request_mapping("/d/<int:pk>", method="get")
def delete(self, request, pk):
# 해당 리뷰가 로그인된 사람이 쓴 것일 경우에만 삭제 실행, 아닌 경우 게시판 목록으로 이동
try:
obj = Board.objects.get(id=pk, cust_id=request.session['sessionid']) # 로그인 기능 추가시 sessionid로 변경
except:
return redirect('/board')
obj.delete()
return redirect('/board')
4. 업데이트 화면
1) HTML
- 기본적인 틀은 등록 화면과 유사
- Application에서 받아온 context 내 데이터로 기존 데이터를 input 태그에 출력
- 등록 버튼 클릭 시 /board/u/{{objs.id}} 를 통해 글의 ID를 넘겨서 업데이트
- 취소 버튼 클릭 시 clickCancel() 함수 실행(JavaScript에서)
/templates/boardupdate.html
<div class="board_wrap">
<div class="board_title">
<strong>문의 수정</strong>
<p>답변을 빠르고 정확하게 안내해드립니다.</p>
</div>
<form action="/board/u/{{objs.id}}" method="get">
<div class="board_write_wrap">
<div class="board_write">
<div class="title">
<dl>
<dt>제목</dt>
<dd><input type="text" placeholder="제목 입력" name="title" value="{{objs.title}}" required></dd>
</dl>
</div>
<div class="cont">
<textarea placeholder="내용 입력" name="content" required>{{objs.content}}</textarea>
</div>
</div>
<div class="bt_wrap">
<a href="#" class="on"><button type="submit">등록</button></a>
<a href="#" onclick="clickCancel();">취소</a>
</div>
</div>
</form>
</div>
2) CSS
- /static/css/style.css + media.css
3) JavaScript
- clickcancel() : 취소 버튼 클릭 시 확인창 띄우고, 확인 클릭 시, 해당 글 상세 화면으로 이동
/templates/boardupdate.html -> <script>태그
function clickCancel(){
c = confirm('[주의]\n작성한 내용이 저장되지 않았습니다.\n정말 질문 등록을 취소하시겠습니까?');
if(c == true){
location.href = '/board/g/'+{{objs.id}};
}
}
4) Application
① updateview(pk) : 글 ID를 URL로 받아서 해당 객체를 context에 담아서 boardupdate.html과 함께 전송
② update(pk)
- URL로 받은 게시판 ID와 sessionid를 함께 이용하여 객체를 찾아서 업데이트 된 내용을 저장
- sessionid를 함께 이용한 것은 임의로 URL 입력 시, 알 수 없는 이유로 데이터가 변경되는 것을 막기 위함
- 임의로 URL 입력 시 오류화면이 뜨기 때문에 예외 처리를 통해 리스트 화면으로 이동
- 업데이트가 완료되면, 해당 글의 상세 화면으로 이동
/web/views_board.py -> class BoardView(View)
@request_mapping("/uv/<int:pk>", method="get")
def updateview(self, request, pk):
objs = Board.objects.get(id=pk)
context = {
'objs': objs
}
return render(request, 'boardupdate.html', context)
@request_mapping("/u/<int:pk>", method="get")
def update(self, request, pk):
# 임의로 url 입력하여 접근 시 에러 화면 대신 게시판 목록으로 이동
try:
objs = Board.objects.get(id=pk, cust_id=request.session['sessionid'])
except:
return redirect('/board')
title = request.GET['title']
content = request.GET['content']
objs.title = title
objs.content = content
objs.save()
return redirect('/board/g/'+str(pk))
- 끝 -
'프로젝트형 IoT 서비스 개발 4회차 > 인터페이스 개발 프로젝트 - 음식점 리뷰 사이트' 카테고리의 다른 글
2. 화면 구성 - (8) 회원가입 화면 (0) | 2022.03.24 |
---|---|
2. 화면 구성 - (7) 로그인 화면 (0) | 2022.03.22 |
2. 화면 구성 - (5) 고객센터 - 실시간 채팅 상담 (0) | 2022.03.20 |
2. 화면 구성 - (4) 고객센터 - 자주 묻는 질문 (0) | 2022.03.20 |
2. 화면 구성 - (3) 검색 화면 (0) | 2022.03.20 |