Ubuntu 20.04에서 Redis®로 PHP 세션 데이터를 관리하는 방법
![Ubuntu 20.04에서 Redis®로 PHP 세션 데이터를 관리하는 방법](https://harulog.s3.ap-northeast-2.amazonaws.com/forum/202407/1720171342-header.png)
목차
- 소개
- 사전 요구 사항
- PHP용 Redis® 익스텐션 설치
- 테스트 데이터베이스와 테이블 생성
- 회원가입 스크립트 생성
- 사용자 로그인 폼 및 처리 스크립트 생성
- 대시보드 페이지 생성
- 애플리케이션 테스트
- 결론
소개
웹 애플리케이션에서 사용자가 상호 작용하는 경우, 현재 사용자의 상태를 세션(session)이라고 합니다. 세션 데이터를 사용하면 사용자가 로그인한 동안의 신원을 계속 기억할 수 있습니다. 일반적인 웹 애플리케이션에서는 사용자가 로그인 폼에 사용자 이름과 비밀번호를 제출합니다. 그런 다음 애플리케이션은 데이터베이스에서 해당 자격 증명을 찾습니다. 일치하는 경우에만 사용자에게 웹 애플리케이션에 대한 액세스 권한을 부여합니다. 그렇지 않으면 사용자는 액세스 거부 오류를 받습니다.
로그인한 사용자가 웹 애플리케이션에서 다른 페이지를 요청할 수 있기 때문에 세션 데이터를 유지하는 방법을 찾아야 합니다. 이렇게 하면 사용자가 세션의 수명 동안 로그인 자격 증명을 다시 제출하지 않고도 사이트나 웹 애플리케이션을 쉽게 탐색할 수 있습니다. PHP에서 이 기능을 구현하는 가장 좋은 방법은 로그인에 성공한 사용자에게 액세스 토큰을 발급하고 그 토큰을 데이터베이스 테이블에 저장한 다음 HTTP 쿠키의 형태로 사용자의 브라우저로 보내는 것입니다.
최종 사용자의 브라우저가 쿠키 데이터를 받으면 이후의 모든 HTTP 요청 중에서 이를 다시 보냅니다. 이 시점부터는 웹 페이지가 요청될 때마다 데이터베이스에서 액세스 토큰을 확인하여 사용자의 세부 정보를 기억할 수 있습니다.
액세스 토큰을 발급하고 확인하는 전체 과정은 애플리케이션이 데이터베이스에 대량의 데이터를 쓰고 읽어야 하는 것을 필요로 합니다. 이는 HTTP 요청마다 액세스 토큰을 확인하기 위해 데이터베이스로의 왕복이 필요하기 때문에 사용자 경험에 해를 줄 수 있습니다. 이 도전을 극복하고 빠른 응답을 제공하기 위해 Redis®와 같은 인메모리 데이터베이스에 세션 데이터를 캐시하는 것이 좋습니다. 이 가이드에서는 Ubuntu 20.04에서 Redis® 서버를 사용하여 PHP 세션 데이터를 관리하는 방법에 대해 배우게 될 것입니다.
사전 요구 사항
이 튜토리얼을 완료하려면 다음이 필요합니다:
- 고성능 Ubuntu 20.04 서버
- sudo 사용자
- Lamp Stack
- Redis® 서버
PHP용 Redis® 익스텐션 설치
PHP에서 Redis® 서버의 키-값 저장소와 통신하려면 php-redis
라이브러리를 설치해야 합니다. 먼저 서버에 SSH로 접속한 다음 패키지 정보 인덱스를 업데이트합니다.
$ sudo apt update
다음 명령을 실행하여 php-redis
익스텐션을 설치합니다.
$ sudo apt install -y php-redis
새 변경 사항을 로드하기 위해 아파치 웹 서버를 재시작합니다.
$ sudo systemctl restart apache2
PHP에서 Redis® 서버와 상호 작용하기 위해 API를 초기화한 후 다음 단계에서 테스트 데이터베이스와 테이블을 만듭니다.
테스트 데이터베이스와 테이블 생성
이 단계에서는 사용자의 로그인 자격 증명(이름 및 해시된 비밀번호)을 포함하여 사용자의 로그인 자격 증명을 저장하는 샘플 데이터베이스와 테이블을 설정합니다. 먼저 root로 MySQL 서버에 로그인합니다.
$ sudo mysql -u root -p
비밀번호를 입력하고 Enter 키를 눌러 MySQL 서버에 로그인합니다. 그런 다음 다음 명령을 실행하여 sample_cms
데이터베이스를 설정합니다.
mysql> CREATE DATABASE sample_cms;
아래 출력을 확인하여 데이터베이스를 성공적으로 생성했는지 확인합니다.
Query OK, 1 row affected (0.01 sec)
다음으로, PHP 스크립트에서 root 자격 증명을 사용하지 않는 것이 좋기 때문에 sample_cms
데이터베이스를 위한 비-루트 사용자를 생성합니다. 아래 명령을 실행하여 sample_cms_user
를 생성하고 EXAMPLE_PASSWORD
를 강력한 비밀번호로 바꿉니다.
mysql> CREATE USER 'sample_cms_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
GRANT ALL PRIVILEGES ON sample_cms.* TO 'sample_cms_user'@'localhost';
FLUSH PRIVILEGES;
아래 응답을 받으면 sample_cms_user
사용자를 성공적으로 생성한 것입니다.
...
Query OK, 0 rows affected (0.00 sec)
다음으로, USE
명령을 사용하여 새 sample_cms
데이터베이스로 이동합니다.
mysql> USE sample_cms;
아래 출력을 확인하여 새로운 데이터베이스를 선택한 것을 확인합니다.
Database changed
다음으로, system_users
테이블을 설정합니다. user_id
열을 사용하여 각 사용자 계정을 고유하게 식별합니다. 이 필드에 AUTO_INCREMENT
키워드를 사용하여 각 레코드에 대해 새로운 user_id
를 자동으로 생성합니다. 테이블의 많은 수의 사용자를 수용하기 위해 user_id
열에서 BIGINT
데이터 유형을 사용합니다. 마지막으로, username
, first_name
, last_name
, pwd
필드에 VARCHAR
데이터 유형을 사용합니다. ENGINE = InnoDB
키워드를 사용하여 빠르고 트랜잭션 가능한 InnoDB
엔진을 사용할 수 있습니다.
system_users
테이블을 생성하려면 다음 명령을 실행합니다.
mysql> CREATE TABLE system_users (
user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(15),
first_name VARCHAR(50),
last_name VARCHAR(50),
pwd VARCHAR(255)
) ENGINE = InnoDB;
아래 출력을 확인하여 테이블을 성공적으로 생성했는지 확인합니다.
Query OK, 0 rows affected (0.02 sec)
MySQL 명령 줄 인터페이스에서 나옵니다.
mysql> QUIT;
아래 출력에서 Bye
를 확인합니다.
system_users
테이블이 데이터를 수신할 준비가 되었습니다. 다음 단계에서 테이블을 채우기 위한 PHP 스크립트를 만들어 보겠습니다.
회원가입 스크립트 생성
사용자-세션 기능을 테스트하기 위해 몇 가지 예제 레코드가 필요합니다. 이 단계에서는 PHP 스크립트를 설정하여 Linux curl
명령에서 사용자의 데이터를 받은 다음 system_users
테이블을 채웁니다. 프로덕션 환경에서는 사용자가 애플리케이션에 가입할 수 있는 등록 페이지를 만들 수 있습니다. 그러나 이 가이드에서는 등록 양식을 만들지 않고 사용자가 직접 등록 프로세스를 자동화하는 스크립트가 필요합니다.
루트 디렉토리의 /var/www/html/register.php
파일을 연 다음 다음 정보를 파일에 입력합니다.
<?php
try {
$db_name = 'sample_cms';
$db_user = 'sample_cms_user';
$db_password = 'EXAMPLE_PASSWORD';
$db_host = 'localhost';
$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sql = 'insert into system_users
(
username,
first_name,
last_name,
pwd
)
values
(
:username,
:first_name,
:last_name,
:pwd
)
';
$data = [];
$data = [
'username' => $_POST['username'],
'first_name' => $_POST['first_name'],
'last_name' => $_POST['last_name'],
'pwd' => password_hash($_POST['pwd'], PASSWORD_BCRYPT)
];
$stmt = $pdo->prepare($sql);
$stmt->execute($data);
echo "User data saved successfully.
" ;
} catch (PDOException $e) {
echo 'Database error. ' . $e->getMessage();
}
편집을 마친 후에 파일을 저장하고 닫습니다. 파일 상단에서, Step 2
에서 설정한 데이터베이스 변수를 선언합니다. 다음으로, PHP PDO 라이브러리를 사용하여 준비된 문을 실행하여 데이터를 system_users
테이블에 저장합니다. 평문으로 암호를 저장하지 않기 위해 암호화 알고리즘인 bcrypt를 사용하여 비밀번호를 해시하는 password_hash($_POST['pwd'], PASSWORD_BCRYPT)
문을 사용합니다.
다음으로, 다음 curl
명령을 실행하여 데이터베이스에 일부 사용자 계정을 생성합니다. 패스워드에는 ...EXAMPLE_PASSWORD_1...
, ...EXAMPLE_PASSWORD_2...
, ...EXAMPLE_PASSWORD_3...
와 같이 원하는 패스워드로 대체할 수 있습니다.
$ curl --data "username=john_doe&first_name=JOHN&last_name=DOE&pwd=EXAMPLE_PASSWORD_1" http://localhost/register.php
$ curl --data "username=mary_smith&first_name=MARY&last_name=SMITH&pwd=EXAMPLE_PASSWORD_2" http://localhost/register.php
$ curl --data "username=roe_jane&first_name=ROE&last_name=JANE&pwd=EXAMPLE_PASSWORD_3" http://localhost/register.php
각 명령을 실행한 후에는 다음 출력을 받아 사용자 계정을 성공적으로 생성했는지 확인합니다.
...
User data saved successfully.
그 다음, sample_cms_user
로 MySQL 서버에 로그인하여 레코드를 확인합니다. 다음 명령은 sudo
권한이 필요하지 않습니다.
$ mysql -u sample_cms_user -p
sample_cms_user
의 패스워드(예: EXAMPLE_PASSWORD
)를 입력하고 Enter 키를 눌러 진행합니다. 그런 다음, sample_cms
데이터베이스로 이동합니다.
mysql> USE sample_cms;
출력에서 새 데이터베이스를 선택한 것을 확인합니다.
Database changed
다음으로, system_users
테이블에서 SELECT
문을 실행하여 레코드를 확인합니다.
mysql> SELECT
user_id,
username,
first_name,
last_name,
pwd
FROM system_users;
데이터가 올바르게 들어간지 확인하기 위해 다음 출력을 받습니다. pwd
열은 해시되어 있음을 알 수 있습니다.
+---------+------------+------------+-----------+--------------------------------------------------------------+
| user_id | username | first_name | last_name | pwd |
+---------+------------+------------+-----------+--------------------------------------------------------------+
| 1 | john_doe | JOHN | DOE | $2y$10$8WcrxHkCUuRM4upVmYJhe.xKAXpoQkVQahoYI87RAlgSeTaxgq3Km |
| 2 | mary_smith | MARY | SMITH | $2y$10$Yk3ZngColV9WGL4c/mgxvuwaVMutq73NW1mWXMrydoukEUxpq0XA2 |
| 3 | roe_jane | ROE | JANE | $2y$10$TcSaOC6MylunFXI4s.XTW.W70i9XjJIa3VyT2JXBygW4pvSoKvj4y |
+---------+------------+------------+-----------+--------------------------------------------------------------+
3 rows in set (0.01 sec)
MySQL 서버 명령 줄 인터페이스에서 나옵니다.
mysql> QUIT;
출력에서 Bye
를 확인합니다.
샘플 사용자 계정이 준비되면 다음 단계에서 로그인 페이지를 만들겠습니다.
사용자 로그인 폼 및 처리 스크립트 생성
사용자가 샘플 애플리케이션에 액세스하는 방법은 로그인 페이지를 통해 접근하는 것입니다. 이 단계에서는 사용자 이름과 비밀번호를 입력하는 HTML 폼을 만들고 이 폼에서 받은 로그인 자격 증명을 PHP 스크립트로 전송하여 생성한 데이터베이스의 값과 대조하여 사용자를 인증할 것입니다.
새로운 /var/www/html/login.php
파일을 엽니다.
$ sudo nano /var/www/html/login.php
그런 다음, 아래 정보를 /var/www/html/login.php
파일에 입력합니다.
<html>
<head>
<title>User Login</title>
</head>
<body>
<h2>User Login Page</h2>
<form action="/process.php" method="post">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" ><br><br>
<label for="pwd">Password:</label><br>
<input type="password" id="pwd" name="pwd"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
편집을 완료한 후에 파일을 저장하고 닫습니다. 이 파일에서 action="/process.php
문은 데이터를 process.php
파일로 전송하도록 양식에 지시합니다. 다음으로, nano
를 사용하여 새로운 /var/www/html/process.php
파일을 만듭니다.
$ sudo nano /var/www/html/process.php
그런 다음, 아래 정보를 /var/www/html/process.php
파일에 입력합니다.
<?php
try {
$db_name = 'sample_cms';
$db_user = 'sample_cms_user';
$db_password = 'EXAMPLE_PASSWORD';
$db_host = 'localhost';
$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$sql = 'select
user_id,
username,
first_name,
last_name,
pwd
from system_users
where username = :username
';
$data = [];
$data = [
'username' => $_POST['username']
];
$stmt = $pdo->prepare($sql);
$stmt->execute($data);
$user_data = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$user_data = $row;
}
if (password_verify($_POST['pwd'], $user_data['pwd']) == true) {
$session_token = bin2hex(openssl_random_pseudo_bytes(16));
$user_data['token'] = $session_token;
setcookie('token', $session_token, time()+3600);
setcookie('username', $user_data['username'], time()+3600);
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis_key = $user_data['username'];
$redis->set($redis_key, serialize($user_data));
$redis->expire($redis_key, 3600);
header('Location: dashboard.php');
} else {
header('Location: login.php');
}
} catch (PDOException $e) {
echo 'Database error. ' . $e->getMessage();
}
편집을 완료한 후에 파일을 저장하고 닫습니다. 파일 상단에서 Redis® 서버에 연결한 다음, 다음 문을 사용하여 사용자 이름과 일치하는 존재 여부를 확인합니다.
...
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists($_COOKIE['username'])) {
$user_data = unserialize($redis->get($_COOKIE['username']));
if ($_COOKIE['token'] == $user_data['token']) {
echo "Welcome, " . $user_data['first_name'] . ' ' . $user_data['last_name'] . "<br>"
. "Your token is " . $user_data['token'];
} else {
echo "Invalid token.";
}
} else {
echo "Access denied.";
}
...
로그인 페이지는 이제 사용자를 인증합니다. 다음 단계에서 로그인된 사용자가 리디렉션되는 대시보드 페이지를 생성하겠습니다.
대시보드 페이지 생성
웹 애플리케이션에서 대시보드는 메뉴와 링크를 통해 사용자가 탐색할 수 있는 메인 페이지입니다. 로그인 페이지에서 유효한 자격 증명을 입력하고 로그인 버튼을 클릭한 인증된 사용자만 대시보드에 접근할 수 있어야 합니다.
이전 단계에서 액세스 토큰과 사용자 이름을 쿠키에 저장하고 Redis® 서버에 캐시했습니다. 이 단계에서는이 쿠키의 값을 검사하고 Redis® 서버에서 다시 유효성을 검사하여 일치 여부를 확인합니다.
쿠키 정보를 가진 사용자가 대시보드 페이지에 직접 접근하려고 하면 액세스 거부 메시지가 표시됩니다. 또한 쿠키 정보가 수정되어 Redis® 서버의 값과 일치하지 않으면 스크립트에서 Invalid token.
오류로 응답해야 합니다.
nano
텍스트 편집기를 사용하여 새로운 /var/www/html/dashboard.php
파일을 엽니다.
$ sudo nano /var/www/html/dashboard.php
그런 다음 다음 정보를 파일에 입력합니다.
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<h1>Dashboard</h1>
<p>
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists($_COOKIE['username'])) {
$user_data = unserialize($redis->get($_COOKIE['username']));
if ($_COOKIE['token'] == $user_data['token']) {
echo "Welcome, " . $user_data['first_name'] . ' ' . $user_data['last_name'] . "<br>"
. "Your token is " . $user_data['token'];
} else {
echo "Invalid token.";
}
} else {
echo "Access denied.";
}
?>
</p>
</body>
</html>
편집을 완료한 후에 파일을 저장하고 닫습니다. 위 파일에서 exists($_COOKIE['username']))
문을 사용하여 $_COOKIE['username']
값을 갖는 키가 있는지 확인합니다. Redis®에 이 이름으로 캐시된 키가 있으면 브라우저의 액세스 토큰 쿠키 ($_COOKIE['token']
)의 값과 비교합니다. 값이 동일하면 Redis® 서버에서 사용자 정보를 읽어와서 다음 문을 사용하여 사용자의 세부 정보를 표시합니다.
echo "Welcome, " . $user_data['first_name'] . ' ' . $user_data['last_name'] . "<br>"
. "Your token is " . $user_data['token'];
참고로, 사용자의 로그인 상태를 알아야 하는 모든 페이지에서 이 dashboard.php
페이지의 세션 처리 로직을 포함해야 합니다. 다음 단계에서 작성한 모든 코드의 기능을 테스트해 보겠습니다.
애플리케이션 테스트
브라우저에서 다음 페이지로 이동하고 주소를 사용자의 웹 서버의 올바른 공용 IP 주소 또는 도메인 이름으로 바꿉니다.
사용자 이름과 비밀번호를 입력하라는 메시지가 표시됩니다. JOHN DOE
의 자격 증명(예: 사용자 이름:john_doe
, 비밀번호:EXAMPLE_PASSWORD_1
)을 입력하고 제출을 클릭하여 로그인합니다.
이제 http://192.0.2.1/dashboard.php 페이지로 리디렉션되고 환영 메시지를 받을 수 있습니다.
대시보드 페이지를 새로 고치하더라도 로그인된 상태를 유지합니다. 세션 데이터가 1시간
동안 유지되도록 설정했기 때문입니다. 위 스크린샷은 Redis®가 MySQL 데이터베이스를 사용하지 않고 세션 데이터를 처리할 수 있으며, 특히 사용자가 많은 경우 세션 데이터를 캐시하기 때문에 응답 시간을 크게 개선할 수 있음을 보여줍니다.
결론
이 가이드에서는 샘플 데이터베이스와 사용자 테이블을 설정했습니다. 그런 다음 PHP 페이지로 데이터를 영구적으로 유지하기 위해 Redis® 서버에 세션 데이터를 저장하는 방법을 구현했습니다. 이 가이드의 로직을 사용하여 규모 확장이 필요한 고가용성 프로젝트에 대한 세션 관리를 설계하세요.
Redis® 서버는 사용자의 세션 데이터 외에도 더 많은 기능을 제공합니다. Redis® 서버를 사용하여 데이터베이스 및 프런트엔드 부하를 더욱 줄이는 방법에 대해서는 다음 문서를 참조하십시오:
- Ubuntu 20.04에서 Redis®와 PHP를 사용하여 MySQL 데이터 캐시하기
- Ubuntu 20.04에서 PHP를 사용하여 Redis® Queue와 Worker를 구현하는 방법
- Ubuntu 20.04에서 PHP와 함께 Redis®를 사용하여 Rate Limiter로 사용하기
출처: https://docs.vultr.com/how-to-manage-php-session-data-with-redis-on-ubuntu-20-04
안정적이고 저렴한 가상 서버(VPS) 안내
안정적인 서버 호스팅을 찾고 계신가요? 지금 Vultr 호스팅을 한번 이용 해 보세요. 지금 보고 계신 HaruLogs 사이트도 Vultr에서 VPS 호스팅 하고 있습니다. 한국, 일본, 미국등 다양한 국가에 클릭 몇번으로 안정적이고 저렴한 VPS를 운영 할 수 있습니다.
아래 배너를 통해 회원 가입을 하시면 100달러의 무료 크레딧을 받을 수 있습니다. 100달러의 무료 크레딧은 Vultr의 최소 사양 VPS를 20대 동시 운영가능한 크레딧입니다.
Vultr 서비스 요약
- VPS
- 인스턴스
- AMD 또는 Intel High Performance 인스턴스 최저 $6 - 1vCPU, 1GB Memory, 2TB Bandwidth, 25GB NVMe
- 엣지
- 인스턴스
- Block Storage
- Object Storage (AWS S3 호환)
- Firewall
- Network
- Load Balancers
- Kubernetes
- Databases