본문 바로가기

개발과제

[노말틱 모의 해킹 취업반 9주차 개발과제 ] 게시판 만들기(5) - 좋아요기능

안녕하세요! 이번에는 좋아요 기능과 정렬 기능을 만들어 보겠습니다.

 

보안 관련해서 코드가 좀 수정 됐습니다! 여기 참고하세요

 

https://mynameisarke.tistory.com/27

 

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

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

mynameisarke.tistory.com

 

 

먼저 좋아요 기능 만들어 보겠습니다

.

.

.

.

.

일단 게시물 데이터베이스에 좋아요을 따로 저장이 가능한 칼럼을 만들겠습니다.

 

이렇게 좋아요 수를 저장 할 수 있게 했습니다.

 

 

한 각 사용자 들은 좋아요 도배를 못 하게 한 번만 누를 수 있게 하겠습니다!

만약 누른 상태로 또 누르면 삭제되게 하겠습니다.

 

이걸 하기 위해 각 사용자들은 좋아요 누른 게시물 ID을 저장하게 하겠습니다.

 

 

나중에 부족하면 더욱 크게 잡도록 할게요

 

 

LOGIN_INFO 데이터베이스에 추가하겠습니다.

.

.

.

.

.

 

이제 board.php랑 view_board.php에 좋아요 수도 볼 수 있게 만들겠습니다.

 

-board.php

 

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

            
            //검색이 입력된 경우
            if(isset($_GET['search'])) {
                $search = mysqli_real_escape_string($conn, filter_var(strip_tags($_GET['search']),FILTER_SANITIZE_SPECIAL_CHARS));
                $date_value = mysqli_real_escape_string($conn, $_GET['date_value']);
                $date_value = DateTime::createFromFormat('Y-m-d', $_GET['date_value'])->format('Y-m-d'); //날짜 값으로 다시 바꾼다
                
                if(isset($_GET['date_value'])) {
                    //날짜 선택한 경우
                    $sql = "SELECT board_id,id,title,date_value,views,like_value 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,like_value FROM $db_board WHERE title LIKE '%$search%'";
                    $total_sql = "SELECT count(*) AS cnt FROM $db_board WHERE title LIKE '%$search%'";
                }
            }

 

검색, 기본 조회 sql문에 like_value을 추가했습니다

 

            if(mysqli_num_rows($result) > 0) {
                //게시물 출력
                while($row = mysqli_fetch_assoc($result)) {
                    $index = $row['board_id'];
                    $id = $row['id'];
                    $title = $row['title'];
                    $like_value = $row['like_value'];
                    echo '<p><a href="view_board.php?index='.$index.'">'.$title.'</a> : '.$id.'작성함 조회수: '.$row['views'].' 좋아요 수: '.$like_value.'</p>';
                }
            } else {
                echo "게시물이 없습니다!";
            }

또한

중간 게시물 출력에 수정했습니다.

 

 

-view_board.php

 

            //게시물 출력
            while($row = mysqli_fetch_assoc($result)) {
                echo '<p>조회수: '.$row['views'].'</p>';
                echo '<p>좋아요: '.$row['like_value'].'</p>';
                echo '<p>ID: '.$row['id'].'</p>';
                echo '<p>Title: '.$row['title'].'</p>';
                echo '<p>Detail: '.$row['detail'].'</p>';
                echo '<p>Date: '.$row['date_value'].'</p>';
                .
                .
                .

while 구문에 한 줄 추가했습니다.

 

 

 

 

한 번 봐볼게요!

 

 

 

친구왈: 너무 dirty합니다

 

 

일단.. 기능만 만들어 놓고 나중에 css까지 겸해서 이쁘게 만들겠습니다.

이제 거의 다 만들어 가니깐요!

 

 

난 깔끔한데

 

 

어쨌든 view_board.php와 board.php에 잘 나오는 것을 확인했습니다!

 

이제 좋아요 누르면 좋아요 수가 늘어나게 하겠습니다!

view_board.php에 새로운 버튼을 만들겠습니다!

 

                //게시물 좋아요 버튼을 보여준다
                echo "<form method='POST' action='like.php'>
                        <input type='hidden' name='board_id' value='".$row['board_id']."'>
                        <p><button type='submit'>좋아요!</button></p>
                    </form>";

 

 

깔끔..한데..

 

 

 

이제 like.php을 만들어서 like_value가 올라가게 만들겠습니다!

 

늘 그랬다시피 데이터베이스 연결하고 세션도 열고 없으면 나가게 했습니다.

 

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

    //데이터베이스 연결(LOGIN_INFO)
    $conn = mysqli_connect($host,$username,$password,$dbname); //로그인
    $conn_for_board = 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(); //이 페이지를 바로 닫는다
    }

 

그리고 로그인 ID와 POST 받은 게시물 ID을 저장하겠습니다.

 

    //세션ID 저장
    $login_id = $_SESSION['login_id'];
    //POST 받은 id값 저장
    $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['board_id']),FILTER_SANITIZE_SPECIAL_CHARS));

 

 

그다음에 sql문을 작성하겠습니다!

 

    //ID통해 좋아요 누른 게시물 조회
    $sql = "SELECT likes_board FROM LOGIN_INFO WHERE id = '$login_id' AND ( FIND_IN_SET('$board_id',likes_board ) > 0 )";

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

 

FIND_IN_SET은 문자 구분 자을 통해 구별을 하고 해당 문자열을 찾는 겁니다!

여기서 문자 구분자는 , 을 뜻합니다.

 

 

예시로 FIND_IN_SET('b' , 'a,b,c,d') 을 하면 

b을 찾고 있습니다! a,b,c,d 에서요! 

 

라는 뜻입니다.

그럼 , 로 나누어서 찾아봅니다 b가 결국 2번째에 위치하잖아요?

 

그럼 이 함수는 2를 반환하게 됩니다!

 

다시 돌아와서 제가 작성한 sql문을 보시면

 

( FIND_IN_SET('$board_id',likes_board ) > 0 

 

이 말은 $board_id을 찾고 있습니다! likes_board 에서요!라는 뜻이겠죠

 

그리고 만약 있으면 어디에 있는지 최소 1번째이니 1은 반환하겠죠?

그래서 0보다 크면 참이게 했습니다.

 

참이란 이야기는 있다는 이야기가 되니깐요!

거짓이면 0이라는 이야기니 없다는 뜻이겠죠!

 

 

 

이제 if else로 작성하겠습니다

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

    if(mysqli_num_rows($result) > 0) {
        //좋아요 누른 경우 취소하기
        mysqli_query($conn, "UPDATE LOGIN_INFO SET likes_board = REPLACE(likes_board, '$board_id,', '') WHERE id = '$login_id'");
        mysqli_query($conn_for_board, "UPDATE BOARD_INFO SET like_value = like_value - 1 WHERE board_id = '$board_id' ");
    }
    else {
        //좋아요 안 누른 경우 추가
        mysqli_query($conn,"UPDATE LOGIN_INFO SET likes_board = CONCAT_WS('$board_id', 'likes_board', ',') WHERE id = '$login_id'");
        mysqli_query($conn_for_board, "UPDATE BOARD_INFO SET like_value = like_value + 1 WHERE board_id = '$board_id' ");
    }
    
    header("Location: view_board.php?index=".$board_id);

    exit();

 

 

REPLACE는 말 그대로 대신 위치하는 겁니다!

그러니깐 likes_board = REPLACE(likes_board, '$board_id,', '') 이 말은

 

likes_board에 '%board_id,'(콤마도 포함해야되요) 을 대신 ''( 빈칸입니다 작은따옴표 두 개 붙인 겁니다.) 대체하는 겁니다!

 

그리고 CONCAT_WS도 처음 보신 거예요

CONCAT_WS('$board_id', `likes_board`, ',') 이 뜻은

, 으로 구분하며 likes_board에 $board_id을 추가하겠습니다! 이런 뜻입니다.

 

그러니깐 결국 likes_board에 있는 값들에 추가로 뒤에 $board_id, 붙어서 생기게 됩니다!

 

이제 됐습니다 실행해보겠습니다.

 

그전에

 

너무 지저분하네요

 

싹 다 비우겠습니다

 

CSS는 나중에 적용해도 안 늦겠죠!

 

그리고 각각 다른 로그인 ID로 게시물을 만들겠습니다!

 

이제 좋아요 눌르겠습니다

 

올라가네요!
저장도 잘 되는게 보입니다

 

또다시 누르면?

 

쨘!

 

잘 사라지네요

 

이렇게 구현했습니다!

 

다음에는 정렬순을 구현하겠습니다

날짜별, 제목순, 작성자별, 추천수(좋아요) 별으로 각자 선택해서 볼 수 있게 하겠습니다!

 

오늘은 이렇게 간단하게 만들고 마치도록 하겠습니다.

.

.

.

.

-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,filter_var(strip_tags(intval($_GET['page'])),FILTER_SANITIZE_SPECIAL_CHARS));
            } else {
                $page = 1;
            }

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

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

            
            //검색이 입력된 경우
            if(isset($_GET['search'])) {
                $search = mysqli_real_escape_string($conn, filter_var(strip_tags($_GET['search']),FILTER_SANITIZE_SPECIAL_CHARS));
                $date_value = mysqli_real_escape_string($conn, $_GET['date_value']);
                $date_value = DateTime::createFromFormat('Y-m-d', $_GET['date_value'])->format('Y-m-d'); //날짜 값으로 다시 바꾼다
                
                if(isset($_GET['date_value'])) {
                    //날짜 선택한 경우
                    $sql = "SELECT board_id,id,title,date_value,views,like_value 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,like_value 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'];
                    $like_value = $row['like_value'];
                    echo '<p><a href="view_board.php?index='.$index.'">'.$title.'</a> : '.$id.'작성함 조회수: '.$row['views'].' 좋아요 수: '.$like_value.'</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>

 

 

-view_board.php

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>게시물 보는중</title>
    </head>
    <body>
        <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(); //이 페이지를 바로 닫는다
            }

            //GET방식으로 전달된 index 받는다
            $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_GET['index']),FILTER_SANITIZE_SPECIAL_CHARS));
            //가독성 위해 다른 변수에 저장
            $last_viewed_time_for_board_id = 'last_view_time_'.$board_id;

            // 세션에 마지막 조회 시간 정보가 있는지 확인
            if (!isset($_SESSION[$last_viewed_time_for_board_id])) {
                // 세션에 마지막 조회 시간 정보가 없으면 현재 시간을 저장
                $_SESSION[$last_viewed_time_for_board_id] = time();
            } else {
                // 세션에 마지막 조회 시간 정보가 있으면 일정 시간이 지났는지 확인
                $last_view_time = $_SESSION[$last_viewed_time_for_board_id];
                $current_time = time();
                $time_diff = $current_time - $last_view_time;
                if ($time_diff >= 60) { // 60초(1시간) 이상 지났으면 조회수 증가
                //조회수 증가하는 sql문
                $sql_view = "UPDATE $db_board SET views = views + 1 WHERE board_id = '$board_id'";
                //실행
                $result_view = mysqli_query($conn, $sql_view);

                // 세션에 현재 조회 시간을 저장
                $_SESSION[$last_viewed_time_for_board_id] = $current_time;
                }
            }

            //작성된 게시물들 조회 문
            $sql = "SELECT * FROM $db_board WHERE board_id = '$board_id' ";
            //쿼리문 실행
            $result = mysqli_query($conn, $sql);
            
            //게시물 출력
            while($row = mysqli_fetch_assoc($result)) {
                echo '<p>조회수: '.$row['views'].'</p>';
                echo '<p>좋아요: '.$row['like_value'].'</p>';
                echo '<p>ID: '.$row['id'].'</p>';
                echo '<p>Title: '.$row['title'].'</p>';
                echo '<p>Detail: '.$row['detail'].'</p>';
                echo '<p>Date: '.$row['date_value'].'</p>';
                if (isset($row['file_name'])) {
                    $file_name = implode('_', array_slice(explode('_', $row['file_name']), 1)); // '_' 문자를 기준으로 분리 후 첫 번째 요소를 제외한 나머지 요소를 모두 합쳐서 파일명을 구성합니다.
                    $download_url = 'download_process.php?filename='.$row['file_name'];
                    echo '<p>FILE: <a href="'.$download_url.'">'.$file_name.'</a></p>';
                }
                
                //로그인한 사용자의 정보 가져온다
                $user_id = $_SESSION['login_id'];

                //게시물 작성자와 로그인한 ID와 일치한 경우
                if($row['id'] == $user_id) {
                    //게시물 수정 버튼을 보여준다
                    echo "<form method='GET' action='fix_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>게시물 수정</button></p>
                          </form>";

                    //게시물 삭제 버튼을 보여준다
                    echo "<form method='POST' action='delete_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>삭제</button></p>
                          </form>";
                }
                
                //게시물 좋아요 버튼을 보여준다
                echo "<form method='POST' action='like.php'>
                        <input type='hidden' name='board_id' value='".$row['board_id']."'>
                        <p><button type='submit'>좋아요!</button></p>
                    </form>";

            }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['write_error'])) {
                    echo $_SESSION['write_error'];
                    unset($_SESSION['write_error']);
                }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['file_error'])) {
                    echo $_SESSION['file_error'];
                    unset($_SESSION['file_error']);
                }
        ?>
        </p>
        <p></p>

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

 

 

 

 

-like.php

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

    //데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password,$dbname); //로그인
    $conn_for_board = 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(); //이 페이지를 바로 닫는다
    }

    //세션ID 저장
    $login_id = $_SESSION['login_id'];
    //POST 받은 id값 저장
    $board_id = mysqli_real_escape_string($conn,filter_var(strip_tags($_POST['board_id']),FILTER_SANITIZE_SPECIAL_CHARS));

    //ID통해 좋아요 누른 게시물 조회
    $sql = "SELECT likes_board FROM LOGIN_INFO WHERE id = '$login_id' AND ( FIND_IN_SET('$board_id',likes_board ) > 0 )";

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

    if(mysqli_num_rows($result) > 0) {
        //좋아요 누른 경우 취소하기
        mysqli_query($conn, "UPDATE LOGIN_INFO SET likes_board = REPLACE(likes_board, '$board_id,', '') WHERE id = '$login_id'");
        mysqli_query($conn_for_board, "UPDATE BOARD_INFO SET like_value = like_value - 1 WHERE board_id = '$board_id' ");
    }
    else {
        //좋아요 안 누른 경우 추가
        mysqli_query($conn, "UPDATE LOGIN_INFO SET likes_board = CONCAT_WS('$board_id', likes_board, ',') WHERE id = '$login_id'");
        mysqli_query($conn_for_board, "UPDATE BOARD_INFO SET like_value = like_value + 1 WHERE board_id = '$board_id' ");
    }
    
    header("Location: view_board.php?index=".$board_id);

    exit();

?>

 

감사합니다!