PHP, Mysql: 좋아요 기능 만들기
예전에 PHP로 만들었던 간단한 CRUD 게시판에 ‘좋아요 기능’을 추가해보도록 하겠습니다. ‘좋아요 기능’은 빈 하트(♡)를 누르면 하트가 채워지면서(♥) 숫자 카운트가 올라가는 기능입니다.
여기서는 로그인 기능이 구현되지 않았으므로 아이피 주소(IP Address)를 기준으로 중복 좋아요를 방지하도록 하겠습니다. 만약 로그인이 구현되어 있다면 로그인한 유저를 기준으로 중복 좋아요를 방지할 수도 있습니다.
이 예제는 phpmyadmin, JQuery, Bootstrap 을 사용합니다.
참고로 게시판의 DB 테이블 구조는 다음과 같습니다.
1) 좋아요 기능 관련 데이터베이스 테이블 (좋아요 테이블) 만들기
아래 구조를 가진 테이블을 생성합니다.
id- 고유 키값입니다.service_code- 각 게시물마다 사용할 수 있는 고유의 코드를 저장합니다. 예를 들어 위의 게시판에서 게시판ID를 기준으로 코드를 만든다면phpex-1,phpex-2이런 식으로 코드를 부여합니다. 또는 코드 대신 게시물 아이디를 저장할 수도 있습니다.liked__ip- 중복 여부를 검사하기 위해 IP를 저장합니다.is_like- 이 부분은 나중에 싫어요 기능을 만드는 것에 대비해 만들어 놓은 컬럼입니다. 1이면 ‘좋아요’, 0이면 ‘싫어요’ 입니다.date- 기능이 실행된 때를 기록하는 컬럼입니다.
2) 게시판 테이블에 좋아요 카운트를 기록하는 컬럼 생성
좋아요 숫자를 별도로 기록하는 컬럼을 게시판 테이블에 생성합니다. 빨간 박스는 새로 생성한 컬럼입니다.
1
SELECT count(*) FROM like_table WHERE service_code = 'XXX'
사실 좋아요 카운트 컬럼을 별도로 기록하지 않고 위의 좋아요 테이블에서 count(*) 문을 사용하여 갯수를 셀 수도 있습니다만, 굳이 별도의 컬럼을 추가한 이유는 다음과 같습니다.
count를 사용하는 경우, 게시판 초기 로딩 시 카운트 수를 불러오기가 매우 어려워집니다. 일일히 게시글 수 만큼count작업을 수행해야 하며 단일JOIN으로 처리하기도 어렵습니다.- 만일 처리가 가능하더라도 반복된 SELECT 호출로 인해 성능 저하를 일으킬 수 있습니다.
이러한 이유로 번거로워 보이지만 별도의 카운트를 저장하는 컬럼을 생성한 뒤, 좋아요 버튼이 클릭되면 좋아요 테이블에 새로운 아이피를 기록함과 동시에 게시판 테이블에도 좋아요 카운트 1을 늘리고, 반대로 좋아요를 취소한 경우 좋아요 테이블의 기록을 삭제하고 게시판 테이블에도 좋아요 카운트 1을 감소시키는 형태로 진행하도록 하겠습니다.
3) 좋아요 버튼 생성
하트 텍스트나 이모지 기호를 사용하든, 이미지를 사용하든, Font Awesome 등 별도의 이모티콘 라이브러리를 사용하든 자유입니다만, 여기서는 최대한 빠르게 작업하기 위해 텍스트를 사용하도록 하겠습니다.
하트 기호가 들어갈 부분에 하이라이트된 코드를 작성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
<?php while($row = mysqli_fetch_array($res)) { ?>
<tr>
<?php
$seq = $row['seq'];
echo "<th scope='row'>";
// ...
echo '<td class="like-container"><button type="button" class="btn-like" data-article-id="'.$seq.'">'
.'<span class="heart-shape">♡</span> <span class="like-count">'.$row['like_count'].'</span></button></td>'
?>
</tr>
<?php }?>
CSS 스타일을 지정합니다.
1
2
3
4
5
6
7
8
9
.btn-like .heart-shape {
display: inline;
color: red;
}
.btn-like {
border: none;
background-color: inherit;
}
아래와 같이 좋아요 버튼 부분이 생긴 것을 확인할 수 있습니다.
4) 좋아요 및 좋아요 취소 부분을 담당하는 JQuery 코드 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(".btn-like").on("click", function(e) {
var button = $(e.currentTarget || e.target)
var likeCount = button.find(".like-count")
var heartShape = button.find(".heart-shape")
$.post("./like_proc.php", {
articleId: button.data("articleId")
}, function(res) {
var addCount = (res == "like" ? 1 : res == "unlike" ? -1 : 0)
likeCount.text(+likeCount.text() + addCount)
heartShape.text(res == "like" ? "♥" : res == "unlike" ? "♡" : "♡")
})
})
php 단에서는 한 개의 POST 파라미터를 받는다고 미리 계획하고 제이쿼리 코드를 작성합니다. 응답 메시지로 "like", "unlike", "failed" 셋 중 하나를 받는다고 가정하고 코드를 작성합니다.
like인 경우 현재 카운터를 1 증가시키고, 하트 모양을 채워져 있는 것으로 변경unlike인 경우 현재 카운터를 1 감소시키고, 하트 모양을 비워져 있는 것으로 변경
작업은 AJAX로 처리하며, $.post를 사용하였습니다. articleId는 php단에서 받을 파라미터입니다.
5) 좋아요 업데이트를 처리하는 PHP 작성
필요한 기능은 아래와 같습니다.
- 좋아요 업데이트를 하는 기능
- 좋아요 취소를 하는 기능
POST 로 처리하도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
include 'initializeDB.php'; // $mysqli 변수 포함
$ip = $ip = $_SERVER['REMOTE_ADDR']; // 사용자의 IP주소 가져오기
$article_id = $_POST['articleId']; // 게시글 아이디
if(!empty($article_id)) {
$sql1 = "SELECT * from dere_like WHERE service_code = 'phpex-$article_id' AND liked_ip = '$ip'";
$res1 = mysqli_num_rows($mysqli->query($sql1)); // sql 의 행 갯수를 가져옴
if($res1 == 0) {
// 좋아요 기록이 없는 경우 -> 좋아요 등록
$sql2 = "INSERT into dere_like VALUES(0, 'phpex-$article_id', '$ip', 1, sysdate())";
$res2 = $mysqli->query($sql2);
// 게시판 테이블 업데이트
$sql3 = "UPDATE messages SET like_count = like_count + 1 WHERE seq = $article_id";
$res3 = $mysqli->query($sql3);
echo $res2 && $res3 ? "like" : "failed";
} else {
// 이미 좋아요를 누른 경우 -> 좋아요 취소
$sql2 = "DELETE from dere_like WHERE service_code = 'phpex-$article_id' AND liked_ip = '$ip'";
$res2 = $mysqli->query($sql2);
// 게시판 테이블 업데이트
$sql3 = "UPDATE messages SET like_count = like_count - 1 WHERE seq = $article_id";
$res3 = $mysqli->query($sql3);
echo $res2 && $res3 ? "unlike" : "failed";
}
}
?>
없어보이는 코드이지만 프로세스를 요약하자면
- 좋아요 여부를 토글하고
- 게시판 테이블의 like_count를 업데이트
입니다. 트랜잭션이 가능한 경우 트랜잭션 처리하여 둘 중 하나만 업데이트 되는 경우를 방지하도록 합니다.
여기까지 하면 좋아요 버튼을 눌렀을 때 좋아요가 추가되거나 감소됩니다. 그리고 실제 데이터베이스에도 반영됩니다.
5) 게시판을 로딩했을 때 내가 좋아요 버튼을 눌렀는지 여부 알아내기
위에서 데이터베이스는 정상적으로 처리가 되지만 새로고침하면 하트 모양 글자는 좋아요 여부와 관계없이 빈 하트가 나타나게 됩니다.
내가 좋아요를 눌렀던 글이라면 처음부터 채워진 하트 모양이 나오도록 하겠습니다.
먼저 내가 좋아요를 눌렀는지 여부를 알려주는 php를 작성하도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include 'initializeDB.php'; //$mysqli 변수 포함
$ip = $ip = $_SERVER['REMOTE_ADDR']; // 사용자의 IP주소 가져오기
$article_id = $_POST['articleId']; // 게시글 아이디
$service_code = $_GET['getLikedByCode'];
if(!empty($article_id)) {
// ....
} else if(!empty($service_code)) {
$sql1 = "SELECT * from dere_like WHERE service_code = 'phpex-$service_code' AND liked_ip = '$ip'";
$res1 = mysqli_num_rows($mysqli->query($sql1)); // sql 의 행 갯수를 가져옴
echo $res1 != 0 ? "liked" : "unliked";
}
?>
좋아요 기록이 있다면 liked를, 없다면 unliked를 반환합니다.
다음으로 Heart 버튼을 display: none; 처리하도록 하겠습니다. 이것을 하는 이유는, 만약 로딩이 늦어졌을 때 결과가 뒤늦게 반영되는 것을 방지하고, AJAX 결과를 받아왔을 때 동시에 표시되도록 하여 혼란을 방지하도록 하기 위함입니다.
1
2
3
4
5
.btn-like {
display: none;
border: none;
background-color: inherit;
}
다음으로 JQuery 부분입니다.
1
2
3
4
5
6
7
8
9
10
$(".btn-like").each(function(idx, el) {
var button = $(el)
var heartShape = button.find(".heart-shape")
$.get("./like_proc.php", {
getLikedByCode: button.data("articleId")
}, function(res) {
heartShape.text(res == "liked" ? "♥" : "♡")
button.fadeIn(200)
})
})
each() 함수를 이용해 버튼을 순회하면서 $.get으로 해당 게시글에 내가 하트를 눌렀는지 여부를 가져오고, 그 여부에 따라 하트 모양을 결정합니다. 그 다음 fadeIn()을 적용해 좋아요 버튼이 서서히 표시가 되도록 합니다.
이렇게 하면 페이지를 처음 로딩했을 경우에도 좋아요 여부가 반영됩니다.
https://media.giphy.com/media/9CObWj8mpquhD2boq8/giphy.mp4









