데이터베이스: 시작하기
소개
거의 모든 최신 웹 애플리케이션은 데이터베이스와 상호 작용합니다. Laravel은 원시 SQL, 유창한 쿼리 빌더 및 Eloquent ORM을 사용하여 지원되는 다양한 데이터베이스에서 데이터베이스와 상호 작용하는 것을 매우 간단하게 만듭니다. 현재 Laravel은 다음 5가지 데이터베이스에 대한 퍼스트 파티 지원을 제공합니다.
또한 MongoDB는 MongoDB에서 공식적으로 관리하는 mongodb/laravel-mongodb 패키지를 통해 지원됩니다. 자세한 내용은 Laravel MongoDB 문서를 확인하세요.
구성
Laravel의 데이터베이스 서비스 구성은 애플리케이션의 config/database.php 구성 파일에 있습니다. 이 파일에서 모든 데이터베이스 연결을 정의하고 기본적으로 사용할 연결을 지정할 수 있습니다. 이 파일 내의 대부분의 구성 옵션은 애플리케이션의 환경 변수 값에 의해 구동됩니다. Laravel에서 지원하는 대부분의 데이터베이스 시스템에 대한 예제가 이 파일에 제공됩니다.
기본적으로 Laravel의 샘플 환경 구성은 로컬 컴퓨터에서 Laravel 애플리케이션을 개발하기 위한 Docker 구성인 Laravel Sail과 함께 사용할 준비가 되어 있습니다. 그러나 로컬 데이터베이스에 필요한 경우 데이터베이스 구성을 자유롭게 수정할 수 있습니다.
SQLite 구성
SQLite 데이터베이스는 파일 시스템의 단일 파일 내에 포함됩니다. 터미널에서 touch 명령을 사용하여 새 SQLite 데이터베이스를 만들 수 있습니다: touch database/database.sqlite. 데이터베이스가 생성된 후 DB_DATABASE 환경 변수에 데이터베이스의 절대 경로를 배치하여 이 데이터베이스를 가리키도록 환경 변수를 쉽게 구성할 수 있습니다:
DB_CONNECTION=sqliteDB_DATABASE=/absolute/path/to/database.sqlite
기본적으로 SQLite 연결에는 외래 키 제약 조건이 활성화되어 있습니다. 이를 비활성화하려면 DB_FOREIGN_KEYS 환경 변수를 false로 설정해야 합니다.
DB_FOREIGN_KEYS=false
Laravel 설치 프로그램을 사용하여 Laravel 애플리케이션을 만들고 SQLite를 데이터베이스로 선택하면 Laravel은 자동으로 database/database.sqlite 파일을 생성하고 기본 데이터베이스 마이그레이션을 실행합니다.
Microsoft SQL Server 구성
Microsoft SQL Server 데이터베이스를 사용하려면 sqlsrv 및 pdo_sqlsrv PHP 확장 프로그램과 Microsoft SQL ODBC 드라이버와 같이 필요한 종속성이 설치되어 있는지 확인해야 합니다.
URL을 사용한 구성
일반적으로 데이터베이스 연결은 host, database, username, password 등과 같은 여러 구성 값을 사용하여 구성됩니다. 이러한 각 구성 값에는 해당하는 환경 변수가 있습니다. 즉, 프로덕션 서버에서 데이터베이스 연결 정보를 구성할 때 여러 환경 변수를 관리해야 합니다.
AWS 및 Heroku와 같은 일부 관리형 데이터베이스 공급자는 단일 문자열로 데이터베이스에 대한 모든 연결 정보를 포함하는 단일 데이터베이스 "URL"을 제공합니다. 데이터베이스 URL의 예는 다음과 같습니다.
mysql://root:[email protected]/forge?charset=UTF-8
이러한 URL은 일반적으로 다음의 표준 스키마 규칙을 따릅니다.
driver://username:password@host:port/database?options
편의를 위해 Laravel은 여러 구성 옵션으로 데이터베이스를 구성하는 대신 이러한 URL을 지원합니다. url(또는 해당하는 DB_URL 환경 변수) 구성 옵션이 있는 경우 데이터베이스 연결 및 자격 증명 정보를 추출하는 데 사용됩니다.
읽기 및 쓰기 연결
경우에 따라 SELECT 문에는 하나의 데이터베이스 연결을 사용하고, INSERT, UPDATE, DELETE 문에는 다른 데이터베이스 연결을 사용하고 싶을 수 있습니다. Laravel은 이를 매우 쉽게 처리하며, 원시 쿼리, 쿼리 빌더 또는 Eloquent ORM을 사용하는지 여부에 관계없이 항상 적절한 연결이 사용됩니다.
읽기/쓰기 연결을 구성하는 방법을 보려면 다음 예제를 살펴보겠습니다.
'mysql' => [ 'read' => [ 'host' => [ '192.168.1.1', '196.168.1.2', ], ], 'write' => [ 'host' => [ '196.168.1.3', ], ], 'sticky' => true, 'database' => env('DB_DATABASE', 'laravel'), 'username' => env('DB_USERNAME', 'root'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => env('DB_CHARSET', 'utf8mb4'), 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 'prefix' => '', 'prefix_indexes' => true, 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), ]) : [],],
구성 배열에 read, write, sticky의 세 가지 키가 추가되었습니다. read 및 write 키에는 단일 키인 host를 포함하는 배열 값이 있습니다. read 및 write 연결에 대한 나머지 데이터베이스 옵션은 기본 mysql 구성 배열에서 병합됩니다.
기본 mysql 배열의 값을 재정의하려는 경우에만 항목을 read 및 write 배열에 배치하면 됩니다. 따라서 이 경우 192.168.1.1은 "읽기" 연결의 호스트로 사용되고, 192.168.1.3은 "쓰기" 연결에 사용됩니다. 데이터베이스 자격 증명, 접두사, 문자 집합 및 기본 mysql 배열의 다른 모든 옵션은 두 연결 모두에서 공유됩니다. host 구성 배열에 여러 값이 있는 경우 각 요청에 대해 데이터베이스 호스트가 무작위로 선택됩니다.
sticky 옵션
sticky 옵션은 현재 요청 주기 동안 데이터베이스에 기록된 레코드를 즉시 읽을 수 있도록 하는 데 사용할 수 있는 선택적 값입니다. sticky 옵션이 활성화되어 있고 현재 요청 주기 동안 데이터베이스에 대해 "쓰기" 작업이 수행된 경우, 추가 "읽기" 작업은 "쓰기" 연결을 사용합니다. 이를 통해 요청 주기 동안 작성된 모든 데이터를 동일한 요청 중에 데이터베이스에서 즉시 다시 읽을 수 있습니다. 이것이 애플리케이션에 대해 원하는 동작인지 여부를 결정하는 것은 귀하에게 달려 있습니다.
SQL 쿼리 실행
데이터베이스 연결을 구성했으면 DB 파사드를 사용하여 쿼리를 실행할 수 있습니다. DB 파사드는 각 쿼리 유형에 대한 메서드(select, update, insert, delete, statement)를 제공합니다.
SELECT 쿼리 실행
기본 SELECT 쿼리를 실행하려면 DB 파사드에서 select 메서드를 사용할 수 있습니다.
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller;use Illuminate\Support\Facades\DB;use Illuminate\View\View; class UserController extends Controller{ /** * 애플리케이션의 모든 사용자 목록을 표시합니다. */ public function index(): View { $users = DB::select('select * from users where active = ?', [1]); return view('user.index', ['users' => $users]); }}
select 메서드에 전달되는 첫 번째 인수는 SQL 쿼리이고, 두 번째 인수는 쿼리에 바인딩해야 하는 모든 매개변수 바인딩입니다. 일반적으로 이는 where 절 제약 조건의 값입니다. 매개변수 바인딩은 SQL 인젝션으로부터 보호합니다.
select 메서드는 항상 결과의 array를 반환합니다. 배열 내의 각 결과는 데이터베이스의 레코드를 나타내는 PHP stdClass 객체입니다.
use Illuminate\Support\Facades\DB; $users = DB::select('select * from users'); foreach ($users as $user) { echo $user->name;}
스칼라 값 선택
데이터베이스 쿼리가 단일 스칼라 값을 생성할 수 있는 경우가 있습니다. 레코드 객체에서 쿼리의 스칼라 결과를 검색해야 하는 대신 Laravel을 사용하면 scalar 메서드를 사용하여 이 값을 직접 검색할 수 있습니다.
$burgers = DB::scalar( "select count(case when food = 'burger' then 1 end) as burgers from menu");
여러 결과 집합 선택
애플리케이션이 여러 결과 집합을 반환하는 저장 프로시저를 호출하는 경우, selectResultSets 메서드를 사용하여 저장 프로시저에서 반환된 모든 결과 집합을 검색할 수 있습니다.
[$options, $notifications] = DB::selectResultSets( "CALL get_user_options_and_notifications(?)", $request->user()->id);
이름 바인딩 사용
매개변수 바인딩을 나타내기 위해 ?를 사용하는 대신 이름 바인딩을 사용하여 쿼리를 실행할 수 있습니다.
$results = DB::select('select * from users where id = :id', ['id' => 1]);
INSERT 문 실행
insert 문을 실행하려면 DB 파사드에서 insert 메서드를 사용할 수 있습니다. select와 마찬가지로 이 메서드는 첫 번째 인수로 SQL 쿼리를, 두 번째 인수로 바인딩을 받습니다.
use Illuminate\Support\Facades\DB; DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
UPDATE 문 실행
update 메서드는 데이터베이스에서 기존 레코드를 업데이트하는 데 사용해야 합니다. 문에 의해 영향을 받은 행 수는 메서드에서 반환됩니다.
use Illuminate\Support\Facades\DB; $affected = DB::update( 'update users set votes = 100 where name = ?', ['Anita']);
DELETE 문 실행
delete 메서드는 데이터베이스에서 레코드를 삭제하는 데 사용해야 합니다. update와 마찬가지로 영향을 받은 행 수는 메서드에서 반환됩니다.
use Illuminate\Support\Facades\DB; $deleted = DB::delete('delete from users');
일반 문 실행
일부 데이터베이스 문은 값을 반환하지 않습니다. 이러한 유형의 작업의 경우 DB 파사드에서 statement 메서드를 사용할 수 있습니다.
DB::statement('drop table users');
준비되지 않은 문 실행
경우에 따라 값을 바인딩하지 않고 SQL 문을 실행하고 싶을 수 있습니다. DB 파사드의 unprepared 메서드를 사용하여 이를 수행할 수 있습니다.
DB::unprepared('update users set votes = 100 where name = "Dries"');
준비되지 않은 문은 매개변수를 바인딩하지 않으므로 SQL 인젝션에 취약할 수 있습니다. 준비되지 않은 문 내에서 사용자 제어 값을 허용해서는 안 됩니다.
암시적 커밋
트랜잭션 내에서 DB 파사드의 statement 및 unprepared 메서드를 사용할 때는 암시적 커밋을 유발하는 문을 피하도록 주의해야 합니다. 이러한 문은 데이터베이스 엔진이 전체 트랜잭션을 간접적으로 커밋하도록 하여 Laravel이 데이터베이스의 트랜잭션 수준을 인식하지 못하게 합니다. 이러한 문의 예는 데이터베이스 테이블을 만드는 것입니다.
DB::unprepared('create table a (col varchar(1) null)');
암시적 커밋을 트리거하는 모든 문 목록은 MySQL 설명서를 참조하십시오.
여러 데이터베이스 연결 사용
애플리케이션이 config/database.php 구성 파일에 여러 연결을 정의하는 경우, DB 파사드에서 제공하는 connection 메서드를 통해 각 연결에 액세스할 수 있습니다. connection 메서드에 전달되는 연결 이름은 config/database.php 구성 파일에 나열된 연결 중 하나와 일치하거나 config 헬퍼를 사용하여 런타임에 구성해야 합니다.
use Illuminate\Support\Facades\DB; $users = DB::connection('sqlite')->select(/* ... */);
연결 인스턴스에서 getPdo 메서드를 사용하여 연결의 원시 기본 PDO 인스턴스에 액세스할 수 있습니다.
$pdo = DB::connection()->getPdo();
쿼리 이벤트 수신
애플리케이션에서 실행되는 각 SQL 쿼리에 대해 호출되는 클로저를 지정하려면 DB 파사드의 listen 메서드를 사용할 수 있습니다. 이 메서드는 쿼리 로깅 또는 디버깅에 유용할 수 있습니다. 서비스 제공자의 boot 메서드에서 쿼리 수신 클로저를 등록할 수 있습니다.
<?php namespace App\Providers; use Illuminate\Database\Events\QueryExecuted;use Illuminate\Support\Facades\DB;use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { DB::listen(function (QueryExecuted $query) { // $query->sql; // $query->bindings; // $query->time; // $query->toRawSql(); }); }}
누적 쿼리 시간 모니터링
최신 웹 애플리케이션의 일반적인 성능 병목 현상은 데이터베이스를 쿼리하는 데 걸리는 시간입니다. 고맙게도 Laravel은 단일 요청 중에 데이터베이스를 쿼리하는 데 너무 많은 시간을 소비할 때 사용자가 선택한 클로저 또는 콜백을 호출할 수 있습니다. 시작하려면 쿼리 시간 임계값(밀리초)과 클로저를 whenQueryingForLongerThan 메서드에 제공합니다. 서비스 제공자의 boot 메서드에서 이 메서드를 호출할 수 있습니다.
<?php namespace App\Providers; use Illuminate\Database\Connection;use Illuminate\Support\Facades\DB;use Illuminate\Support\ServiceProvider;use Illuminate\Database\Events\QueryExecuted; class AppServiceProvider extends ServiceProvider{ /** * 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) { // 개발 팀에 알립니다... }); }}
데이터베이스 트랜잭션
DB 파사드에서 제공하는 transaction 메서드를 사용하여 데이터베이스 트랜잭션 내에서 일련의 작업을 실행할 수 있습니다. 트랜잭션 클로저 내에서 예외가 발생하면 트랜잭션이 자동으로 롤백되고 예외가 다시 발생합니다. 클로저가 성공적으로 실행되면 트랜잭션이 자동으로 커밋됩니다. transaction 메서드를 사용하는 동안 수동으로 롤백하거나 커밋하는 것에 대해 걱정할 필요가 없습니다.
use Illuminate\Support\Facades\DB; DB::transaction(function () { DB::update('update users set votes = 1'); DB::delete('delete from posts');});
데드락 처리
transaction 메서드는 데드락이 발생할 때 트랜잭션을 재시도해야 하는 횟수를 정의하는 선택적 두 번째 인수를 허용합니다. 이러한 시도가 소진되면 예외가 발생합니다.
use Illuminate\Support\Facades\DB; DB::transaction(function () { DB::update('update users set votes = 1'); DB::delete('delete from posts');}, 5);
수동으로 트랜잭션 사용
수동으로 트랜잭션을 시작하고 롤백 및 커밋을 완벽하게 제어하려면 DB 파사드에서 제공하는 beginTransaction 메서드를 사용할 수 있습니다.
use Illuminate\Support\Facades\DB; DB::beginTransaction();
rollBack 메서드를 통해 트랜잭션을 롤백할 수 있습니다.
DB::rollBack();
마지막으로 commit 메서드를 통해 트랜잭션을 커밋할 수 있습니다.
DB::commit();
DB 파사드의 트랜잭션 메서드는 쿼리 빌더와 Eloquent ORM 모두에 대한 트랜잭션을 제어합니다.
데이터베이스 CLI에 연결
데이터베이스의 CLI에 연결하려면 db Artisan 명령을 사용할 수 있습니다.
php artisan db
필요한 경우, 기본 연결이 아닌 데이터베이스 연결에 연결하기 위해 데이터베이스 연결 이름을 지정할 수 있습니다:
php artisan db mysql
데이터베이스 검사하기
db:show 및 db:table Artisan 명령어를 사용하여 데이터베이스 및 관련 테이블에 대한 유용한 정보를 얻을 수 있습니다. 데이터베이스 크기, 유형, 열린 연결 수 및 테이블 요약을 포함한 데이터베이스 개요를 보려면 db:show 명령어를 사용할 수 있습니다:
php artisan db:show
--database 옵션을 통해 명령에 데이터베이스 연결 이름을 제공하여 검사할 데이터베이스 연결을 지정할 수 있습니다:
php artisan db:show --database=pgsql
명령어 출력에 테이블 행 수 및 데이터베이스 뷰 세부 정보를 포함하려면 각각 --counts 및 --views 옵션을 제공할 수 있습니다. 대규모 데이터베이스에서는 행 수 및 뷰 세부 정보를 검색하는 데 시간이 오래 걸릴 수 있습니다:
php artisan db:show --counts --views
또한, 다음 Schema 메소드를 사용하여 데이터베이스를 검사할 수 있습니다:
use Illuminate\Support\Facades\Schema; $tables = Schema::getTables();$views = Schema::getViews();$columns = Schema::getColumns('users');$indexes = Schema::getIndexes('users');$foreignKeys = Schema::getForeignKeys('users');
애플리케이션의 기본 연결이 아닌 데이터베이스 연결을 검사하려면 connection 메소드를 사용할 수 있습니다:
$columns = Schema::connection('sqlite')->getColumns('users');
테이블 개요
데이터베이스 내의 개별 테이블에 대한 개요를 얻으려면 db:table Artisan 명령어를 실행할 수 있습니다. 이 명령어는 데이터베이스 테이블의 열, 유형, 속성, 키 및 인덱스를 포함한 일반적인 개요를 제공합니다:
php artisan db:table users
데이터베이스 모니터링
db:monitor Artisan 명령어를 사용하여 데이터베이스가 지정된 수 이상의 열린 연결을 관리하는 경우 Laravel이 Illuminate\Database\Events\DatabaseBusy 이벤트를 디스패치하도록 지시할 수 있습니다.
시작하려면 db:monitor 명령어를 매분 실행되도록 스케줄링해야 합니다. 이 명령어는 모니터링하려는 데이터베이스 연결 구성의 이름과 이벤트를 디스패치하기 전에 허용해야 하는 최대 열린 연결 수를 허용합니다.
php artisan db:monitor --databases=mysql,pgsql --max=100
이 명령어를 스케줄링하는 것만으로는 열린 연결 수에 대한 알림을 트리거하기에 충분하지 않습니다. 명령어가 임계값을 초과하는 열린 연결 수를 가진 데이터베이스를 발견하면 DatabaseBusy 이벤트가 디스패치됩니다. 개발팀 또는 자신에게 알림을 보내기 위해 애플리케이션의 AppServiceProvider 내에서 이 이벤트를 수신해야 합니다.
use App\Notifications\DatabaseApproachingMaxConnections;use Illuminate\Database\Events\DatabaseBusy;use Illuminate\Support\Facades\Event;use Illuminate\Support\Facades\Notification; /** * 애플리케이션 서비스 부트스트랩. */public function boot(): void{ Event::listen(function (DatabaseBusy $event) { ->notify(new DatabaseApproachingMaxConnections( $event->connectionName, $event->connections )); });}