데이터베이스: 시작하기

번역일: 2026년 6월 25일

데이터베이스: 시작하기

소개

현대 웹 애플리케이션 대부분은 데이터베이스와 긴밀하게 연동됩니다. Laravel은 raw SQL, 쿼리 빌더, Eloquent ORM을 통해 다양한 데이터베이스와 손쉽게 상호작용할 수 있는 환경을 제공합니다. 현재 Laravel이 공식 지원하는 데이터베이스는 다음과 같습니다.

MongoDB는 MongoDB에서 공식 관리하는 mongodb/laravel-mongodb 패키지를 통해 지원됩니다. 자세한 내용은 Laravel MongoDB 문서를 참고하세요.

설정

데이터베이스 설정은 config/database.php 파일에서 관리합니다. 이 파일에서 모든 데이터베이스 커넥션을 정의하고, 기본으로 사용할 커넥션을 지정할 수 있습니다. 대부분의 설정 값은 .env 환경 변수를 통해 제어되며, 지원하는 각 데이터베이스 시스템의 예시 설정도 파일 안에 포함되어 있습니다.

기본 환경 설정은 Laravel Sail(로컬 개발용 Docker 환경)과 함께 바로 사용할 수 있도록 준비되어 있지만, 로컬 환경에 맞게 자유롭게 수정할 수 있습니다.

SQLite 설정

SQLite 데이터베이스는 파일시스템의 단일 파일로 관리됩니다. 터미널에서 touch database/database.sqlite 명령으로 새 SQLite 파일을 생성한 뒤, 환경 변수에 해당 파일의 절대 경로를 지정하면 됩니다.

DB_CONNECTION=sqlite DB_DATABASE=/absolute/path/to/database.sqlite

SQLite 커넥션에서는 기본적으로 외래 키 제약이 활성화되어 있습니다. 비활성화하려면 아래와 같이 설정하세요.

DB_FOREIGN_KEYS=false

NOTE

Laravel 설치 관리자로 프로젝트를 생성할 때 SQLite를 선택하면, database/database.sqlite 파일이 자동으로 생성되고 기본 데이터베이스 마이그레이션도 자동 실행됩니다.

Microsoft SQL Server 설정

Microsoft SQL Server를 사용하려면 sqlsrvpdo_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?options

Laravel은 이러한 URL 형식도 지원합니다. url 설정 값(또는 DB_URL 환경 변수)이 있으면 커넥션 정보와 인증 정보를 자동으로 파싱해 사용합니다.

읽기 / 쓰기 커넥션

SELECT 쿼리와 INSERT/UPDATE/DELETE 쿼리에 서로 다른 데이터베이스 커넥션을 사용하고 싶을 때가 있습니다. Laravel은 이를 간편하게 지원하며, raw 쿼리, 쿼리 빌더, Eloquent ORM 모두 적절한 커넥션을 자동으로 선택합니다.

아래는 읽기/쓰기 커넥션 설정 예시입니다.

'mysql' => [ 'driver' => 'mysql', 'read' => [ 'host' => [ '192.168.1.1', '196.168.1.2', ], ], 'write' => [ 'host' => [ '192.168.1.3', ], ], 'sticky' => true, 'port' => env('DB_PORT', '3306'), '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([ (PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), ]) : [], ],

설정 배열에 read, write, sticky 세 가지 키가 추가된 것을 확인할 수 있습니다. readwrite는 각각 host 키를 포함하는 배열이며, 나머지 설정(인증 정보, 문자셋 등)은 상위 mysql 배열에서 공유됩니다.

즉, readwrite에는 상위 설정을 재정의할 항목만 적으면 됩니다. 위 예시에서 읽기 커넥션은 192.168.1.1 또는 192.168.1.2(요청마다 무작위 선택), 쓰기 커넥션은 192.168.1.3을 사용합니다.

`sticky` 옵션

sticky는 선택적 옵션으로, 현재 요청 사이클 내에서 쓰기 작업이 발생한 경우 이후 읽기 작업도 쓰기 커넥션을 통해 수행하도록 합니다.

예를 들어, 같은 요청 안에서 데이터를 INSERT한 뒤 바로 SELECT할 때 레플리케이션 지연으로 인해 방금 쓴 데이터가 읽기 서버에서 보이지 않는 문제를 방지할 수 있습니다. 이 동작이 애플리케이션에 적합한지는 직접 판단하여 사용하세요.

SQL 쿼리 실행

데이터베이스 커넥션 설정이 완료되면 DB 파사드를 통해 쿼리를 실행할 수 있습니다. DB 파사드는 select, update, insert, delete, statement 메서드를 제공합니다.

SELECT 쿼리 실행

기본적인 SELECT 쿼리는 DB::select 메서드로 실행합니다.

<?php namespace App\Http\Controllers; 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 메서드를 사용하면 객체에서 값을 꺼낼 필요 없이 바로 받을 수 있습니다.

$count = DB::scalar( "select count(case when food = '김밥' then 1 end) as cnt 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 메서드로 실행합니다.

use Illuminate\Support\Facades\DB; DB::insert('insert into users (id, name) values (?, ?)', [1, '홍길동']);

UPDATE 실행

update 메서드는 레코드를 수정하며, 영향 받은 행 수를 반환합니다.

use Illuminate\Support\Facades\DB; $affected = DB::update( 'update users set votes = 100 where name = ?', ['김철수'] );

DELETE 실행

delete 메서드는 레코드를 삭제하며, 영향 받은 행 수를 반환합니다.

use Illuminate\Support\Facades\DB; $deleted = DB::delete('delete from users');

일반 구문 실행

반환 값이 없는 구문(예: DDL)은 statement 메서드를 사용합니다.

DB::statement('drop table users');

준비되지 않은 구문 실행

파라미터 바인딩 없이 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; // 실행 시간 (ms) // $query->toRawSql(); // 바인딩이 적용된 완성된 SQL }); } }

누적 쿼리 시간 모니터링

단일 요청에서 데이터베이스 쿼리에 너무 많은 시간이 소요될 경우, Laravel이 지정한 콜백을 자동으로 호출하도록 설정할 수 있습니다. whenQueryingForLongerThan 메서드에 임계값(밀리초)과 콜백을 전달하면 됩니다.

<?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'); });

데드락 처리

transaction 메서드의 두 번째 인수로 데드락 발생 시 재시도 횟수를 지정할 수 있습니다. 지정한 횟수를 모두 소진하면 예외가 발생합니다.

use Illuminate\Support\Facades\DB; DB::transaction(function () { DB::update('update users set votes = 1'); DB::delete('delete from posts'); }, attempts: 5);

수동 트랜잭션 제어

트랜잭션의 시작, 롤백, 커밋을 직접 제어하려면 각각의 메서드를 사용합니다.

use Illuminate\Support\Facades\DB; DB::beginTransaction();
DB::rollBack();
DB::commit();

NOTE

DB 파사드의 트랜잭션 메서드는 쿼리 빌더Eloquent ORM 모두에 적용됩니다.

데이터베이스 CLI 접속

Artisan의 db 명령으로 데이터베이스 CLI에 바로 접속할 수 있습니다.

php artisan db

기본 커넥션이 아닌 다른 커넥션에 접속하려면 커넥션 이름을 지정합니다.

php artisan db mysql

데이터베이스 검사

db:showdb: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 명령을 사용합니다.

php artisan db:table users

데이터베이스 모니터링

db:monitor Artisan 명령을 사용하면 데이터베이스의 열린 커넥션 수가 지정한 임계값을 초과할 때 Illuminate\Database\Events\DatabaseBusy 이벤트가 발생하도록 설정할 수 있습니다.

먼저 이 명령을 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 )); }); }

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 25일