본문 바로가기

개발과제

[노말틱 모의 해킹 취업반 9주차 개발과제 ] 게시판 만들기(5) - 날짜 검색

안녕하세요! 오늘은 날짜 지정해서 검색을 할 수 있게 하겠습니다.

 

 

** 주소 검색도 그냥 제가 우체국 DB 가져와서 만들겠습니다. API는 내버려두고 따로 php 또 만들겠습니다. 나중에요!

** 이제 슬슬 게시판도 끝나 가고 다 만들고 난 뒤에 마이 페이지, 문의 게시판까지 만든 뒤에

    CSS나 다른 언어 이용해서 제 홈페이지를 조금 꾸미겠습니다! 너무 스켈레톤이라고 하네요 ㅎ

 

아마 늦어도 2주 뒤에는 제 홈페이지 전부 완성 될 것 같습니다. 그 후로 디자인이랑 보안 신경 써서 수정하죠.

.

.

.

.

.

.

시작하죠 먼저 날짜를 지정해서 검색을 할 수 있게 하겠습니다.

 

뭐.. 이거는 딱히 어렵진 않겠네요!

 

일단 저는 날짜 선택 가능한 UI을 가져오겠습니다!

그냥 검색 창에 날짜를 적고 검색 누르기엔 너무 투박하잖아요

 

저는 검색 버튼 옆에 구현하겠습니다.

 

        <form method ="POST" action="board.php">
            <input type = "text" name="search" placeholder="검색">
            <input type="submit" value="검색">
            <label for="date">날짜 선택:</label>
            <input type="date" id="date_value" name="date,value">
        </form>

 

board.php에

거의 맨 위에 있습니다

 

label에 for='date' 로 날짜 선택 가능하게 했습니다!

그리고 담은 변수명(id)은 date_value로 했습니다

 

 

저장하고 실행해보겠습니다.

 

저는 좋은데.. 남들에 보기엔 디자인이 별로인가봐요

 

이렇게 했습니다!

 

그리고

board.php에 검색하는 구문을 수정하기 전에 제가 데이터베이스에 날짜 이름을 date로 했었죠?

 

그걸 변경하겠습니다.

MYSQL에 쓰이는 언어 함수중 date가 있어서 가독성이 매우 떨어지네요!

 

이렇게요! view_board에 date을 date_value로 바꾸셔야 됩니다! 맨 밑에 총 코드 보여드렐게요

 

 

그리고 검색 sql문을 조금 손 보겠습니다.

 



            //검색이 입력된 경우
            if(isset($_POST['search'])) {
                $search = mysqli_real_escape_string($conn, $_POST['search']);
                $date_value = mysqli_real_escape_string($conn, $_POST['date_value']);
                
                if(isset($_POST['date_value'])) {
                    //날짜 선택한 경우
                    $sql = "SELECT board_id,id,title,date_value,views FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                }
                else {
                    //날짜가 선택이 안된 경우
                    $sql = "SELECT board_id,id,title,views FROM $db_board WHERE title LIKE '%$search%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%'";
                }
            }

 

날짜가 선택이 된 경우 sql문에 date_value까지 찾게 했습니다 LIKE 구절을 이용해서요!

그리고 !empty 함수를 없앴습니다 왜냐하면 검색은 없고 날짜만 검색한 경우가 있기 때문에요.

 

그리고 실행하겠습니다.

 

 

4월 18일 누르고 아무런 검색 없이 버튼 누르겠습니다

 

 

쨘! 어?

 

일단 잘 나오네요! 밑에 페이징 번호도 조금 있다가 수정하겠습니다.

 

행여나 게시물 보기 눌렀는데 잘 나오네요

 

 

근데 뒤로가기 버튼 누르면 이런 에러가 나옵니다

 

이거는 저희가 작성한 내용이 있었잖아요? 근데 그게 사라지게 될 거라고 경고를 하는 겁니다!

 

참고는

https://azurealstn.tistory.com/121

 

양식 다시 제출 확인 팝업창

개발을 하면서 어드민 페이지에서 ‘새로고침’이나 ‘뒤로가기’를 눌렀을 때 아래와 같은 팝업창이 나와서 당황했습니다. 그렇다면 왜! 이러한 팝업창이 뜬걸까?? 원인은 간단합니다. 원인이

azurealstn.tistory.com

 

여기서 했습니다 간단히 말하자면 페이지 사이를 드나들 때 대부분 GET 방식을 사용한다고 해요.

근데 저희는 POST로 해서 이러한 오류가 발생한다고 하네요!!

 

 

 

 

이게 은근히 거슬리네요 안 나오게 하겠습니다

 

간단합니다! POST을 GET 방식으로 바꾸기만 하면 됩니다!

 

 

 

-어? 보안상 안 좋다고 하시지 않으셨어요??

그렇죠! 근데 페이지 넘길 때만 GET방식을 쓸 겁니다! 그리고 SQL Injection 공격 방지 함수도 쓰고 있어

크게 걱정 안 하셔도 됩니다! 그리고 계속 개발하다가 보안상 문제가 생기면 그때 또 수정하겠습니다.

 

GET방식을 POST로 바꾸어서 $_GET으로 바꿉시다

 

조금 많아서 총 코드 맨 밑에 참고하시면 돼요!

 

 

다시 해보겠습니다.

 

18일로 하고 해보곘습니다

 

아! 참고로 GET으로 받은 변수들은 전부 escape 함수를 씌었습니다

 

잘나오네요!

 

새 로고침해도 양식 제출 확인이 안 나오는 것을 알 수 있습니다

 

 

처음에 코딩을 할 땐 POST 방식이 안전한 줄 알고 전부 POST 방식으로 했지만 그렇게 하다 보니

저렇게 양식 다시 제출하는 오류가 발생하게 되네요!

그래서 페이지가 넘어갈 때는 POST보단 GET방식을 선호한다고 합니다.

 

저는 아마 게시물의 메인 페이지인 board.php 랑 문의게시판, 마이게시판 각 한 페이지씩 GET 방식을 사용 할거 같습니다!

 

당연히 보안을 가장 신경 쓰면서요. GET방식의 최대 단점이 쉽게 데이터 값을 바꿀 수 있다는 거죠.

즉 SQL Injection에 약하다는 이야기입니다. 그러니 저는 GET 방식으로 받은 것들은 전부 escape 함수 이용해서 방지했습니다.

 

시간이 많이 늦어서 내일에 이어서 코딩을 하겠습니다! 감사합니다!

 

 

-board.php

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Welcome!</title>
    </head>
    <body>
        <h1>MENU</h1>
        <form method ="GET" action="board.php">
            <input type = "text" name="search" placeholder="검색">
            <input type="submit" value="검색">
            <label for="date">날짜 선택:</label>
            <input type="date" id="date_value" name="date_value">
        </form>
        <p>
        <?php 

            include 'DB_INFO.php'; //데이터 베이스 정보

            //데이터베이스 연결
            $conn = mysqli_connect($host,$username,$password,$db_board);

            //데이터베이스 오류시 종료
            if(mysqli_connect_errno()) {
                die("데이터 베이스 오류: ". mysqli_connect_error());
            }

            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }

            //한 페이지에 보여줄 게시물 수
            $num_per_page = 5;

            //현재 페이지 번호
            if(isset($_GET['page'])) {
                $page = mysqli_real_escape_string($conn,intval($_GET['page']));
            } else {
                $page = 1;
            }

            //전체 게시물 수 조회
            $total_sql = "SELECT count(*) AS cnt FROM $db_board";

            //작성된 게시물들 조회 문
            $sql = "SELECT board_id,id,title,views FROM $db_board";

            
            //검색이 입력된 경우
            if(isset($_GET['search'])) {
                $search = mysqli_real_escape_string($conn, $_GET['search']);
                $date_value = mysqli_real_escape_string($conn, $_GET['date_value']);
                
                if(isset($_GET['date_value'])) {
                    //날짜 선택한 경우
                    $sql = "SELECT board_id,id,title,date_value,views FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%' AND date_value LIKE '%$date_value%'";
                }
                else {
                    //날짜가 선택이 안된 경우
                    $sql = "SELECT board_id,id,title,views FROM $db_board WHERE title LIKE '%$search%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%'";
                }
            }
            
            $total_result = mysqli_query($conn,$total_sql); //sql문 실행
            $total_row = mysqli_fetch_assoc($total_result); //결과를 배열로 가져온다
            $total_posts = $total_row['cnt']; //배열 중 cnt의 값을 가져온다

            //전체 페이지 수
            $total_pages = ceil($total_posts / $num_per_page);

            //각 페이지 시작 인덱스
            $start = ($page - 1) * $num_per_page;


            //sql문에 추가로 작성하고 LIMIT 설정하고 DESC 즉 내림차순으로 정렬
            $sql .= " ORDER BY board_id DESC LIMIT $start, $num_per_page";

            //쿼리문 실행
            $result = mysqli_query($conn, $sql);

            if(mysqli_num_rows($result) > 0) {
                //게시물 출력
                while($row = mysqli_fetch_assoc($result)) {
                    $index = $row['board_id'];
                    $id = $row['id'];
                    $title = $row['title'];
                    echo '<p><a href="view_board.php?index='.$index.'">'.$title.'</a> : '.$id.'작성함 조회수: '.$row['views'].'</p>';
                }
            } else {
                echo "게시물이 없습니다!";
            }
            
            echo '<p> [ ';
            
            // 현재 페이지를 10으로 나눈 몫에 10을 곱한 값에서 1을 빼면
            // 현재 페이지가 몇 번째 페이지 블록에 있는지 알 수 있습니다.
            $block_start = floor(($page-1)/10)*10;

            if ($block_start > 1) {
                // 이전 페이지 블록이 있다면, 다음 버튼을 출력합니다.
                $next_block_start = $block_start - 10;
                if($next_block_start == 0) {
                    $next_block_start += 1; //0페이지는 존재 하지 않기에 1더한다
                }
                echo '<a href="?page=' . $next_block_start .'"><< </a>';
            }

            // 이전 버튼 출력
            if ($page > 1) {
                echo '<a href="?page=' . ($page-1) .'">< </a> ';
            }
            
            // 페이지 링크 출력
            for ($i = $block_start+1; $i <= min($block_start+10, $total_pages); $i++) {
                if($i == $page) {
                    echo '<strong>'.$i.' </strong>';
                } else {
                    echo '<a href="?page=' . $i .'">'.$i. '</a> ';
                }
                echo ' ';
            }

            // 다음 버튼 출력
            if ($page < $total_pages) {
                echo '<a href="?page=' . ($page+1) .'">> </a> ';
            }
            
            if ($block_start+10 < $total_pages) {
                // 다음 페이지 블록이 있다면, 다음 버튼을 출력합니다.
                $next_block_start = $block_start + 11;
                echo '<a href="?page=' . $next_block_start .'">>></a>';
            }

            echo ' ]</p>';
            
        ?>
        </p>
        <p></p>
        <form action="write_board.php" method="POST"> 
            <p><input type="submit" name="write" value="게시판 작성"></p>
        </form>
        <form action="only_login.php"> 
            <p><input type="submit" value="메인페이지로"></p>
        </form>
    </body>
</html>

 

 

 

 

-write_process_board.php

 

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    session_start(); //세션 시작

    if(!isset($_SESSION['login_id'])) {
        //로그인하지 않은 사용자
        header("Location: login.php"); //login 화면으로 바꾼다
        exit(); //이 페이지를 바로 닫는다
    }

    //게시판 데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password, $db_board);

    //데이터베이스 오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }

    //POST로 전달된 정보 받기
    if(isset($_POST['board_id'])){
        $board_id = mysqli_real_escape_string($conn,$_POST['board_id']);
    } else {
        $board_id = null;
    }
    $title = mysqli_real_escape_string($conn,$_POST['title']);
    $detail = mysqli_real_escape_string($conn,$_POST['detail']);

    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file'])) {
        // 업로드된 파일 정보 가져오기
        $file_name = $_FILES['file']['name'];
        $timestamp = time(); // 현재 시간을 초로 반환
        $new_file_name = $timestamp . '_' . $file_name; // 현재 시간과 원래 파일 이름을 합쳐 새로운 파일 이름 생성
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif','text/plain','application/zip','application/x-hwp','application/msword','application/vnd.ms-excel','application/pdf']; //MIME 허락 된 것
        //확장자 허락 된 것
        $allowed_extensions = array("jpg","png","gif","txt","zip","hwp","word","xls","xlsx","pdf");

        //sql공격 방지용
        $new_file_name = mysqli_real_escape_string($conn,$new_file_name);

        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {
            // 파일 MIME 타입 확인, 조작됬는지 확인
            $file_mime_type = mime_content_type($file_tmp_name);
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $file_mime_type = finfo_file($finfo, $file_tmp_name);
            finfo_close($finfo);
            
            if (in_array($file_mime_type, $allowed_mime_types)) {

                //파일 확장자 확인
                $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
                if(in_array($file_ext, $allowed_extensions)) {
                // 파일 저장 경로
                $upload_path = '/path/to/upload/directory/' . $new_file_name;

                    // 파일 이동 및 저장
                    if (move_uploaded_file($file_tmp_name, $upload_path)) {
                        $_SESSION['file_error'] =  '파일 업로드 성공';
                    } else {
                        $_SESSION['file_error'] =  '파일 업로드 실패';
                    }
                } else {
                    $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
                }
            } else {
                $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
            }
        }
    }
    //session으로 유저 이름 받기
    $user_id = $_SESSION['login_id'];
    echo $board_id;
    if(isset($board_id)) {
        //board_id가 있다는 것은 수정을 의미
        $sql = "UPDATE $db_board SET title = '$title', detail='$detail', file_name='$new_file_name' WHERE board_id='$board_id' ";
    }
    else {
        //board_id가 없으니 새로 만드는 sql문
        $sql = "INSERT INTO $db_board (id, title, detail,file_name) VALUES ('$user_id', '$title', '$detail','$new_file_name')";
    }

    //sql문 실행
    if(mysqli_query($conn,$sql)) {
        //수정인 경우 바로 게시판 보이게 함
        if(isset($board_id)) {
            $_SESSION['write_error'] = '수정되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
        else {
            $board_id = mysqli_insert_id($conn); // 새로 생성한 게시물의 id를 가져옴
            $_SESSION['write_error'] = '작성되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
    } else {
        $_SESSION['write_error'] = '작성 중 오류가 발생하였습니다.';
        header("Location: view_board.php");
    }

    exit();
?>

 

 

 

 

 

**4/23일 수정내용 보안 관련해서 추가 코딩을 했습니다! 총 코드는 밑에 포스트 들어가시면 있어요

 

https://mynameisarke.tistory.com/27

 

[노말틱 모의 해킹 취업반 개발과제 (3)] 보안 관련해서 보완 하기

안녕하세요! 저는 이태까지 GET방식이든 POST 방식이든 일단 받으면 mysqli escape 시켜서 SQL Injection을 피하려고 했습니다! 근데 이제 스크립트 인젝션을 방지하기 위해 filter_var() 함수을 이용하겠습

mynameisarke.tistory.com