728x90

MDB(Meterial Design for Bootstrap4) 게시판을 만드는 골격과 jQuery 내용이다.

Bootstrap4 기본 문법 사항은 https://www.w3schools.com/bootstrap4/default.asp 에 잘 나와 있다.

그리고 MDB 는 https://mdbootstrap.com/ 를 수시로 참조하면 템플릿 구성을 깔끔하게 만들 수 있다.

유료 템플릿을 구입하여 사용하고 있기 때문에 무료용으로 사용할 경우에는 동작 안되는 것이 있을 수 있으니 관련 Class를 수정해서 사용하면 될 것이다.


파일을 여러개로 나누어서 필요한 파일을 Load 하여 사용하는 방법으로 구현했다.

ㅇ 상대경로 자동 설정 : https://link2me.tistory.com/1197 참조

ㅇ MDB Layout 알아둘 사항 : https://link2me.tistory.com/1593 참조

ㅇ PHP Class 개념 이해 : https://link2me.tistory.com/1164 참조.

    PHP Class 함수는 스스로 작성해야 하며, 타 게시글을 찾다보면 이해하게 될 것으로 본다.

    게시판 구현에 필요한 대부분의 코드는 기록했지만 PHP Class 는 오픈하지 않았다.

    스스로 만들어야 할 영역이라고 보면 된다.

    DBDataClass.php 에 대한 사항은 https://link2me.tistory.com/1680 에 오픈했다.

ㅇ 페이징 처리 : https://link2me.tistory.com/1112 참조하되, bootstrap4 기반으로 Class 변경하는 건 직접 하시라.

    PHP Class 작성에 대한 일부 코드를 확인할 수 있다.


bbs.php 파일

<!DOCTYPE html>
<head>
<?php
require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'sessionChk.php'; // 세션 체크
require_once $g['path_config'].'config.php';
require_once $g['path_layout'].'default/_import.head.php';
require_once $g['path_root'].'deviceChk.php';
?>
</head>

<body class="fixed-sn mdb-skin">
<header>
    <!-- Navbar -->
    <nav class="navbar fixed-top navbar-toggleable-md navbar-expand-lg scrolling-navbar double-nav">
        <div class="breadcrumb-dn mr-auto">
            <p><?=$hostName;?></p>
        </div>
        <ul class="nav navbar-nav nav-flex-icons ml-auto">
            <?php require_once $g['path_menu'].'item/item_login.php' ?>
        </ul>
    </nav>
    <!-- /.Navbar -->
</header>

<main>
    <div class="container-fluid text-center">
        <div class="row">
            <div class="col-md-12">
                <div class="content" id="panel_content">
                <!-- 불러온 파일이 이곳에 위치되도록 jQuery 처리 -->
                </div>
                <div id="ajaxPath" data-path="<?=$g['path_menu'];?>"></div>
            </div>
        </div>
    </div>
</main>
<!--/Main layout-->

<!-- SCRIPTS -->
<?php require_once $g['path_layout'].'default/_import.tail.php';?>
<script>
// SideNav Initialization
$(".button-collapse").sideNav();
new WOW().init();

var uri = (uri != null)? uri : "bbsList.php";
var datapath =$("#ajaxPath").attr('data-path');
var bbsid = (bbsid != null)? bbsid : "toadmin";

BBSListTable(uri,bbsid,1,'','',0);

$('.memberShow').on('click',function(e){
    e.preventDefault(); // a 링크, submit 실행 방지
    url = $(this).attr('href');
    var item = $(this).text();
    if(url != '#'){
        $('#panel_content').load(url, function(){
            ModifyData();
        });
    }
});

function fixedEncodeURIComponent (str) {
    // encodeURIComponent 함수는 IE8 이상의 브라우저에서 모두 지원한다.
    return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
}

function BBSListTable(uri,bbsid,curPage,where,keyword,idx){
    $('#panel_content').load(uri+'?bid='+bbsid+'&p='+curPage+'&where='+where+'&keyword='+fixedEncodeURIComponent(keyword)+'&uid='+idx, function() {
        //e.preventDefault();
        var curPage = $('#paging .act a').text();
        $('#paging li').click(function(e) {
            e.preventDefault();
            switch($(this).text()){
                case '◁':
                    curPage=parseInt($(this).next().text()) - 1;
                    break;
                case '▷':
                    curPage=parseInt($(this).prev().text()) + 1;
                    break;
                default:
                    curPage = $(this).text();
                    break;
            }
            BBSListTable(uri,bbsid,curPage,where,keyword,0);
        });

        $('#BBSListTable tbody tr').mouseover(function() {
            $(this).children().css({
                'backgroundColor' : '#DCDCDC', 'cursor' : 'pointer'
            });
        }).mouseout(function() {
            $(this).children().css({
                'backgroundColor' : '#FFFFFF', 'cursor' : 'default'
            });
        }).click(function() {
            var idx = $(this).attr('id');
            var bbsid =$("#bbsid").attr('data-bbsid');
            uri = "bbsView.php";
            BBSListTable(uri,bbsid,curPage,where,keyword,idx);
        });

        $('#BBSSearch').click(function(e){
            BBSSearch(uri,bbsid,curPage,where,keyword);
        });

        $('#BBSSearchKeyword').on('keypress', function(event){
            var agent = navigator.userAgent.toLowerCase();
            var keycode = (event.keyCode ? event.keyCode : event.which);
            if(keycode == 13){ // 엔터키가 입력될 때까지는 어떤 것도 하지 않는다.
                event.preventDefault(); // 엔터키가 입력되면 현재 이벤트의 기본 동작을 중단한다.
                //event.stopPropagation(); // 현재 이벤트가 상위로 전파되지 않도록 중단한다.
                BBSSearch(uri,bbsid,curPage,where,keyword);
            }
        });

        $('#BBSHome').click(function(e){
            e.preventDefault();
            uri = "bbsList.php";
            BBSListTable(uri,bbsid,1,'','',0);
        });

        $('#bbsWrite').click(function(e){
            e.preventDefault();
            uri = $(this).attr('href');
            var bbsid =$("#bbsid").attr('data-bbsid');
            BBSListTable(uri,bbsid,curPage,where,keyword,0);
        });

        $('#bbsModify').click(function(e){
            e.preventDefault();
            uri = $(this).attr('href');
            var idx = $(this).attr('data-id');
            var page = $(this).attr('curPage');
            BBSListTable(uri,bbsid,page,where,keyword,idx);
        });

        $('#bbsRegister').click(function(){
            var subject = $('#subject');
            var content = $('textarea#content');
            var curPage = $('input[name=p]').val();

            if(subject.val() ==''){
                alert('제목을 입력하세요');
                subject.focus();
                return false;
            }

            if(content.val() ==''){
                alert('내용을 입력하세요');
                content.focus();
                return false;
            }

            $.ajax({
                url:datapath+'bbsWriteChk.php',
                type: 'POST',
                data: $("#bbswriteForm").serializeArray(),
                dataType:'text',
                success:function(msg){
                    if(msg == 1){
                        alert('등록했습니다.');
                        BBSListTable(uri,bbsid,curPage,where,keyword,idx);
                    } else if(msg == 2){
                        alert('수정했습니다.');
                        BBSListTable(uri,bbsid,curPage,where,keyword,idx);
                    } else if(msg==-2){
                        alert('수정권한이 없습니다.');
                    } else {
                        alert('데이터를 다시 한번 확인하세요\n'+msg);
                        return false;
                    }
                },
                error: function(jqXHR, textStatus, errorThrown){
                    alert("arjax error : " + textStatus + "\n" + errorThrown);
                }
            });

        });

        $('#bbsDelete').click(function(e){
            e.preventDefault();
            var idx = $(this).attr('data-id');
            var page = $(this).attr('curPage');
            BBSDelete(uri,bbsid,page,where,keyword,idx);
        });

        $('#comment_form').click(function(e){
            e.preventDefault();
            var comment = $("input[name=comment]");
            if(comment.val() ==''){
                alert('댓글을 입력하세요');
                comment.focus();
                return false;
            }
            var page = $("input[name=p]").val();
            var uid = $("input[name=parentid]").val();

            $.ajax({
                url:datapath+'bbsCommentChk.php',
                type: 'POST',
                data: {
                    mode:$("input[name=mode]").val(),
                    parentid:uid,
                    userID:$("input[name=userID]").val(),
                    userNM:$("input[name=userNM]").val(),
                    comment:$("input[name=comment]").val()
                    },
                dataType:'text',
                success:function(msg){
                    if(msg == 1){
                        alert('등록했습니다.');
                        uri = "bbsView.php";
                        BBSListTable(uri,bbsid,page,where,keyword,uid);
                    } else if(msg==-2){
                        alert('수정권한이 없습니다.');
                        return false;
                    } else {
                        alert('데이터를 다시 한번 확인하세요\n'+msg);
                        return false;
                    }
                },
                error: function(jqXHR, textStatus, errorThrown){
                    alert("arjax error : " + textStatus + "\n" + errorThrown);
                }
            });

        });

        $(".comment_del").click(function(){
            var idx = $(this).parent().parent().attr('id');
            var page = $("input[name=p]").val();
            CommnetDelete(uri,bbsid,page,where,keyword,idx);
        });

        $('#ToMainPage').click(function(){
            window.location.href = $(location).attr('protocol')+"//"+$(location).attr('host');
        });

    });
}

function BBSSearch(uri,bbsid,curPage,where,keyword){
    var where = $('[name=where]').val();
    var keyword = $('[name=keyword]').val();
    if(keyword.length == 0){
        alert('검색어를 입력하세요');
        $('input[name=keyword]').focus();
        return false;
    }
    BBSListTable(uri,bbsid,1,where,keyword,0);
}

function BBSDelete(uri,bbsid,curPage,where,keyword,idx){
    var verify = confirm('삭제하시겠습니까? \n 복구할 수 없습니다.');
    if (verify) {
        $.get('bbsDelete.php?idx='+idx, function(msg) {
            if (msg == 1) {
                alert('삭제되었습니다.');
                uri = "bbsList.php";
                BBSListTable(uri,bbsid,curPage,where,keyword,0);
            } else if(msg == -2){
                alert('삭제 권한이 없습니다.');
            } else {
                alert('삭제중 오류가 발생하였습니다.\n'+msg); // 디버깅 모드
                //alert('삭제중 오류가 발생하였습니다.'); // 운용 모드
            }
        });
    }
}

function CommnetDelete(uri,bbsid,curPage,where,keyword,idx){
    var verify = confirm('삭제하시겠습니까? \n 복구할 수 없습니다.');
    if (verify) {
        $.get('bbsCommentDelete.php?idx='+idx, function(msg) {
            if (msg == 1) {
                uri = "bbsView.php";
                var uid = $("input[name=parentid]").val();
                BBSListTable(uri,bbsid,curPage,where,keyword,uid);
            } else if(msg == -2){
                alert('삭제 권한이 없습니다.');
            } else {
                alert('삭제중 오류가 발생하였습니다.\n'+msg);
                //alert('삭제중 오류가 발생하였습니다.');
            }
        });
    }
}
</script>
</body>
</html>

<?php

// 파일명 : bbsList.php

error_reporting(0);
// 테이블 접속 처리
require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'sessionChk.php';
require_once $g['path_root'].'deviceChk.php';
if($mtype == 3){
    require_once $g['path_root'].'ipFiltering.php';
}
require_once $g['path_config'].'config.php';
require_once $g['path_config'].'dbconnect.php';
require_once $g['path_class'].'dbDataClass.php';
require_once $g['path_class'].'bbsClass.php';
require_once $g['path_class'].'adminClass.php';
$a = new adminClass();
$b = new bbsClass();
$d = new DBDataClass(); // AES_Decode()

$bbsid = isset($_GET['bid']) ? $_GET['bid'] :'toadmin';
$link_url = "bbs.php"; // 현재 실행중인 파일명 가져오기
$page = isset($_GET['page'])? trim($_GET['page']):1;//페이지 변수 설정
$rowsPage = ($mtype == 3) ? 5 : 5; // 한 화면에 표시되는 게시글 수
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$m = isset($_GET['m']) ? $_GET['m'] :'list';

$flddata ="*";// 화면에 출력할 칼럼 발췌
$where = isset($_GET['where']) ? $_GET['where']: '';
$keyword = isset($_GET['keyword']) ? $_GET['keyword']: '';
$xorderby= isset($xorderby) ? $xorderby : 'uid DESC'; // 없는 칼럼인지 꼭 체크하라.

$sqlque = 'display=1';
$uri =isset($_GET['uri'])? $_GET['uri']:'';

if($where && $keyword) {
    if($where == 'subject') $sqlque .= " and (subject LIKE '%".$keyword."%') ";
    if($where == 'userNM') $sqlque .= " and (userNM LIKE '%".$keyword."%') ";
}

$g['url_link']=($m?'m='.$m.'&amp;':'').($where?'where='.$where.'&amp;':'').($keyword?'keyword='.urlencode(stripslashes($keyword)).'&amp;':'');
$g['bbs_reset'] = $link_url.'?'.($m?'m='.$m.'&amp;':'');

$table ='bbs_data'; // 기본 접속 테이블과 동일구조 테이블 처리
$NUM = $d->getDbRows($table,$sqlque); // 전체 게시글수
$TPG = $b->getTotalPage($NUM,$rowsPage);
$rows= $d->getDbArray($table,$sqlque,$flddata,$xorderby,$rowsPage,$curPage);
$i = 0;
?>
<div class="table-responsive text-nowrap">
<div class="float-left">
    <?php if( $keyword ):?><strong>"<?php echo $keyword?>"</strong> 검색결과 : <?php endif?>
    <?php echo number_format($NUM)?>개 (<?php echo $curPage;?>/<?php echo $TPG;?>페이지)
</div>
<div class="float-right">
    <?php if(isset($_SESSION['userID'])):?>
    <a href="bbsWrite.php" class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 " id="bbsWrite">글쓰기</a>
    <?php endif?>
    <div id="bbsid" data-bbsid="<?php echo $bbsid; ?>"></div>
</div>

<table id="BBSListTable" class="table table-striped table-bordered table-hover table-sm" cellspacing="0" width="100%">
    <thead align='center'>
        <tr>
            <?php if($mtype==3):?><th scope="col">No</th><?php endif;?>
            <th scope="col">제목</th>
            <?php if($mtype==3):?><th scope="col">이름</th><?php endif;?>
            <th scope="col">날짜</th>
            <?php if($mtype==3):?><th scope="col">조회수</th><?php endif;?>
        </tr>
    </thead>
    <tbody>
        <?php if($NUM == 0):?>
        <tr>
            <td colspan="5">등록된 게시글이 없습니다.</td>
        </tr>
        <?php else:?>
        <?php foreach($rows as $R):?>
        <?php
            $no = $NUM - (($curPage - 1) * $rowsPage) - $i;
            $i++;
        ?>
        <tr id="<?php echo $R['uid']; ?>">
            <?php if($mtype==3):?><td><?php echo $no;?></td><?php endif;?>
            <td class="text-left"><?php echo $R['subject'];?></td>
            <?php if($mtype==3):?><td><?php echo $R['userNM'];?></td><?php endif;?>
            <td><?php echo substr($R['d_regis'],0,8);?></td>
            <?php if($mtype==3):?><td><?php echo $R['hit'];?></td><?php endif;?>
        </tr>
        <?php endforeach;?>
        <?php endif;?>
    </tbody>
</table>
<div class='form-group'>
    <form name="BBSForm" class="form-inline" action="<?php echo $link_url;?>">
        <input type="hidden" name="m" value="<?php echo $m;?>" />
        <input type="hidden" name="orderby" value="<?php echo $xorderby;?>" />

        <div class="input-group mb-3" style="width:80px;">
            <select name="where" class="browser-default custom-select">
                <option value="subject">제목</option>
                <option value="userNM">등록자</option>
            </select>
        </div>
        <div class="input-group mb-3" style="width:calc(100% - 80px);">
          <input type="text" name="keyword" class="form-control" id="BBSSearchKeyword">
          <div class="input-group-append">
            <button class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 waves-effect" type="button" id="BBSSearch">검색</button>
            <button class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 waves-effect" type="button" id="BBSHome">목록</button>
          </div>
        </div>
    </form>
</div>
<?php $b->PageLinkView($link_url,$NUM,$rowsPage,$curPage,$g['url_link']);?>
<button class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0" id="ToMainPage" type="button">메인화면 전환</button>
</div>

<?php

// 파일명 : bbsView.php

error_reporting(0);
// error_reporting(E_ALL); 
//ini_set("display_errors", 1);

require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'sessionChk.php';
require_once $g['path_root'].'deviceChk.php';
if($mtype == 3){
    require_once $g['path_root'].'ipFiltering.php';
}
require_once $g['path_config'].'config.php';
require_once $g['path_config'].'dbconnect.php';
require_once $g['path_class'].'dbDataClass.php';
require_once $g['path_class'].'bbsClass.php';
$c = new bbsClass();
$d = new DBDataClass();

$bid = isset($_GET['bid']) ? $_GET['bid']: '';
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;

$R = $d->getDbData('bbs_data', 'uid='.$_GET['uid'], '*');
$html = ($R['html'] == 1) ? 'HTML' : 'TEXT';
?>
<table class="table table-bordered table-hover table-sm" cellspacing="0" width="100%">
    <tr>
        <td style="width:70px;">제목</td>
        <td class="text-left"><?php echo $R['subject']?></td>
    </tr>
    <tr>
        <td>내용</td>
        <td class="text-left"><?php echo $c->getContents($R['content'],$html);?></td>
    </tr>
</table>

<?php include_once $g['path_bbs'].'bbsComment.php';?>

<div class="table-responsive text-nowrap">
    <div class="float-left info">
        <button class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 waves-effect" type="button" id="BBSHome">목록</button>
    </div>
    <div class="float-right info">
        <?php if($R['userID'] == $_SESSION['userID']):?>
        <a href="bbsWrite.php" class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 waves-effect" id="bbsModify" data-id="<?=$R['uid'];?>" curPage="<?=$curPage;?>">수정</a>
        <?php endif;?>
        <?php if($R['userID'] == $_SESSION['userID'] || (isset($_SESSION['authID']) && $_SESSION['authID']==1) ):?>
        <button class="btn btn-md btn-outline-default m-0 px-3 py-2 z-depth-0 waves-effect" type="button" id="bbsDelete" data-id="<?=$R['uid'];?>" curPage="<?=$curPage;?>">삭제</button>
        <?php endif;?>
    </div>
</div>

<?php

// 파일명 : bbsWrite.php

error_reporting(0);
// 테이블 접속 처리
require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'sessionChk.php';
require_once $g['path_root'].'deviceChk.php';
if($mtype == 3){
    require_once $g['path_root'].'ipFiltering.php';
}
require_once $g['path_config'].'config.php';
require_once $g['path_config'].'dbconnect.php';
require_once $g['path_class'].'dbDataClass.php';
require_once $g['path_class'].'bbsClass.php';
require_once $g['path_class'].'adminClass.php';
$a = new adminClass();
$b = new bbsClass();
$d = new DBDataClass(); // AES_Decode()

$bid = isset($_GET['bid']) ? $_GET['bid']: '';
$uid = isset($_GET['uid']) ? $_GET['uid']: '';
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$R=$d->getUidData('bbs_data',$uid);

?>
<div class="table-responsive text-nowrap">
<form id="bbswriteForm" method="post" class="text-center border border-light p-5">
    <table class="table table-striped table-bordered table-hover table-sm" cellspacing="0" width="100%">
        <input type="hidden" name="mode" value="write" />
        <input type="hidden" name="p" value="<?php echo $curPage;?>" />
        <input type="hidden" name="bid" value="<?php echo $bid;?>" />
        <input type="hidden" name="uid" value="<?php echo $R['uid'];?>" />
        <input type="hidden" name="userID" value="<?php echo $_SESSION['userID'];?>" />
        <input type="hidden" name="userNM" value="<?php echo $_SESSION['userNM'];?>" />

        <tr>
            <td>제목</td>
            <td><input type="text" name="subject" id="subject" class="form-control mb-4" placeholder="제목을 입력하세요" value="<?=$R['subject'];?>"></td>
        </tr>

        <tr>
            <td>내용</td>
            <td><textarea name="content" id="content" class="form-control mb-4" rows="8"  placeholder="내용을 입력하세요"><?=$R['content'];?></textarea></td>
        </tr>

        <tr>
            <td colspan="2">
            <button class="btn btn-info btn-block my-4" id="bbsRegister" type="submit">등록</button>
            </td>
        </tr>
    </table>
</form>
</div>


블로그 이미지

Link2Me

,