데이터베이스: 시작하기
번역일: 2026년 6월 21일
데이터베이스: 시작하기
소개
거의 모든 현대 웹 애플리케이션은 데이터베이스와 상호작용합니다. Laravel은 Raw SQL, 쿼리 빌더, Eloquent ORM을 통해 다양한 데이터베이스와 간편하게 연동할 수 있도록 지원합니다. 현재 Laravel이 공식적으로 지원하는 데이터베이스는 다음과 같습니다:
- MariaDB 10.3+ (버전 정책)
- MySQL 5.7+ (버전 정책)
- PostgreSQL 10.0+ (버전 정책)
- SQLite 3.26.0+
- SQL Server 2017+ (버전 정책)
MongoDB는 MongoDB 공식 팀이 관리하는 mongodb/laravel-mongodb 패키지를 통해 지원됩니다. 자세한 내용은 Laravel MongoDB 문서를 참고하세요.
설정
데이터베이스 설정 파일은 config/database.php에 위치합니다. 이 파일에서 모든 데이터베이스 커넥션을 정의하고, 기본으로 사용할 커넥션을 지정할 수 있습니다. 대부분의 설정값은 환경 변수(.env)를 통해 관리되며, 지원하는 주요 데이터베이스 시스템에 대한 예시 설정이 파일 안에 포함되어 있습니다.
기본적으로 Laravel의 샘플 환경 설정은 로컬 개발 환경용 Docker 구성인 Laravel Sail에 맞춰져 있습니다. 로컬 환경에 맞게 자유롭게 수정할 수 있습니다.
SQLite 설정
SQLite 데이터베이스는 파일 시스템의 단일 파일로 구성됩니다. 터미널에서 touch database/database.sqlite 명령으로 새 SQLite 데이터베이스 파일을 생성한 뒤, .env에서 해당 파일의 절대 경로를 지정하면 됩니다:
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqliteSQLite 커넥션에서는 기본적으로 외래 키 제약이 활성화되어 있습니다. 비활성화하려면 다음 환경 변수를 설정하세요:
DB_FOREIGN_KEYS=falseNOTE
Laravel 인스톨러로 프로젝트를 생성할 때 SQLite를 선택하면, database/database.sqlite 파일이 자동으로 생성되고 기본 마이그레이션도 자동으로 실행됩니다.
Microsoft SQL Server 설정
Microsoft SQL Server를 사용하려면 sqlsrv와 pdo_sqlsrv PHP 익스텐션이 설치되어 있어야 하며, Microsoft SQL ODBC 드라이버 등의 의존성도 필요합니다.
URL을 이용한 설정
일반적으로 데이터베이스 커넥션은 host, database, username, password 등 여러 설정값을 조합해서 구성합니다. 각각에 대응하는 환경 변수가 존재하기 때문에, 운영 서버에서는 여러 환경 변수를 관리해야 합니다.
AWS, Heroku와 같은 관리형 데이터베이스 서비스는 커넥션 정보를 하나의 "URL" 문자열로 제공하기도 합니다:
mysql://root:password@127.0.0.1/forge?charset=UTF-8이 URL은 일반적으로 다음과 같은 형식을 따릅니다:
driver://username:password@host:port/database?optionsLaravel은 이런 URL 형식을 지원합니다. url 설정값(또는 DB_URL 환경 변수)이 존재하면, 해당 URL에서 커넥션 정보와 인증 정보를 자동으로 파싱합니다.
읽기 / 쓰기 커넥션
SELECT 쿼리와 INSERT/UPDATE/DELETE 쿼리에 각각 다른 데이터베이스 커넥션을 사용하고 싶을 때가 있습니다. Laravel은 이를 간단하게 지원하며, Raw 쿼리, 쿼리 빌더, 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 키를 담은 배열입니다. 나머지 옵션들은 상위 mysql 배열에서 공유됩니다.
즉, read와 write 배열에는 상위 배열의 값을 재정의하고 싶은 항목만 넣으면 됩니다. 위 예시에서 읽기 커넥션은 192.168.1.1 또는 192.168.1.2 중 요청마다 무작위로 하나가 선택되고, 쓰기 커넥션은 192.168.1.3을 사용합니다.
`sticky` 옵션
sticky는 선택적 옵션으로, 현재 요청 사이클 내에서 쓰기 작업이 발생했을 때 이후의 읽기 작업도 쓰기 커넥션을 통해 수행하도록 합니다.
예를 들어, 데이터를 INSERT한 직후에 SELECT를 실행할 때 복제 지연(replication lag)으로 인해 방금 쓴 데이터가 읽히지 않는 문제를 방지할 수 있습니다. 이 동작이 애플리케이션에 적합한지는 직접 판단하여 사용하세요.
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 쿼리이고, 두 번째 인자는 바인딩할 파라미터 배열입니다. 파라미터 바인딩은 SQL 인젝션을 방지합니다.
select 메서드는 항상 결과 배열을 반환하며, 각 항목은 데이터베이스 레코드를 나타내는 PHP stdClass 객체입니다:
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}단일 스칼라 값 조회
쿼리 결과가 단일 값 하나일 때는, 레코드 객체에서 값을 꺼내는 대신 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, '홍길동']);UPDATE 실행
update 메서드는 데이터베이스의 기존 레코드를 수정합니다. 영향받은 행(row) 수를 반환합니다:
use Illuminate\Support\Facades\DB;
$affected = DB::update(
'update users set votes = 100 where name = ?',
['김철수']
);DELETE 실행
delete 메서드는 데이터베이스에서 레코드를 삭제합니다. update와 마찬가지로 영향받은 행 수를 반환합니다:
use Illuminate\Support\Facades\DB;
$deleted = DB::delete('delete from users');일반 구문 실행
반환값이 없는 데이터베이스 구문은 statement 메서드를 사용합니다:
DB::statement('drop table users');
Unprepared 구문 실행
파라미터 바인딩 없이 SQL을 직접 실행하고 싶을 때는 unprepared 메서드를 사용합니다:
DB::unprepared('update users set votes = 100 where name = "홍길동"');
WARNING
unprepared는 파라미터를 바인딩하지 않기 때문에 SQL 인젝션에 취약합니다. 사용자 입력값을 절대 직접 포함시키지 마세요.
트랜잭션 내의 암묵적 커밋
트랜잭션 내에서 statement나 unprepared 메서드를 사용할 때는 암묵적 커밋을 유발하는 구문에 주의해야 합니다. 이러한 구문은 데이터베이스 엔진이 트랜잭션 전체를 자동으로 커밋하게 만들어, Laravel이 트랜잭션 상태를 인식하지 못하게 됩니다. 예를 들어, 테이블 생성 구문이 이에 해당합니다:
DB::unprepared('create table a (col varchar(1) null)');
암묵적 커밋을 유발하는 전체 구문 목록은 MySQL 공식 문서를 참고하세요.
여러 데이터베이스 커넥션 사용
config/database.php에 여러 커넥션이 정의되어 있다면, DB 파사드의 connection 메서드를 통해 특정 커넥션을 선택할 수 있습니다. 전달하는 커넥션 이름은 설정 파일에 정의된 이름과 일치해야 합니다:
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; // 실행된 SQL
// $query->bindings; // 바인딩 값
// $query->time; // 실행 시간 (밀리초)
// $query->toRawSql(); // 바인딩이 포함된 완성된 SQL
});
}
}누적 쿼리 시간 모니터링
단일 요청 내에서 데이터베이스 쿼리에 너무 많은 시간이 소요될 때 알림을 받고 싶다면 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 메서드를 사용하면 여러 작업을 하나의 트랜잭션으로 묶어 실행할 수 있습니다. 클로저 내에서 예외가 발생하면 자동으로 롤백되고, 정상적으로 완료되면 자동으로 커밋됩니다. 수동으로 롤백이나 커밋을 처리할 필요가 없습니다:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});교착 상태(Deadlock) 처리
transaction 메서드는 두 번째 인자로 교착 상태 발생 시 재시도 횟수를 받을 수 있습니다. 재시도 횟수를 모두 소진하면 예외가 발생합니다:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, 5);수동 트랜잭션 관리
롤백과 커밋을 직접 제어하고 싶다면 beginTransaction 메서드로 트랜잭션을 시작할 수 있습니다:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();rollBack 메서드로 트랜잭션을 롤백합니다:
DB::rollBack();
commit 메서드로 트랜잭션을 커밋합니다:
DB::commit();
NOTE
DB 파사드의 트랜잭션 메서드는 쿼리 빌더와 Eloquent ORM 모두에 적용됩니다.
데이터베이스 CLI 접속
Artisan의 db 명령어를 사용하면 데이터베이스 CLI에 바로 접속할 수 있습니다:
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 --viewsSchema 파사드를 통해 프로그래밍 방식으로 데이터베이스 정보를 조회할 수도 있습니다:
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 명령어를 사용하세요:
php artisan db:table users데이터베이스 모니터링
db:monitor Artisan 명령어를 사용하면 열린 커넥션 수가 지정한 임계값을 초과할 때 Illuminate\Database\Events\DatabaseBusy 이벤트를 발생시킬 수 있습니다.
시작하려면 db:monitor 명령어를 1분마다 실행되도록 스케줄링하세요. 모니터링할 커넥션 이름과 허용할 최대 커넥션 수를 옵션으로 전달합니다:
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) {
Notification::route('mail', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}