한줄공지
  • 등록된 공지내용이 없습니다.

Ubuntu 20.04에서 Redis®로 PHP 세션 데이터를 관리하는 방법

2024년 7월 5일
조회수 34
코멘트 0

목차

소개

웹 애플리케이션에서 사용자가 상호 작용하는 경우, 현재 사용자의 상태를 세션(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® 서버를 사용하여 데이터베이스 및 프런트엔드 부하를 더욱 줄이는 방법에 대해서는 다음 문서를 참조하십시오:

 

출처: 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 $100 크레딧 선물 받기

 

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
포스트 통계
지금 보고있는 포스트의 통계 데이터를 확인 해 보세요.
  • 총 조회수
    34
  • 최근 30일 조회수
    9
연관 포스트
코멘트 작성