Laravel Eloquent의 모델 관계 이해하기
라라벨 Eloquent 모델 관계 완벽 가이드
라라벨 Eloquent 모델과 관계는 프레임워크의 핵심 구성 요소입니다. 만약 이러한 개념이 어렵게 느껴지거나, 쉽고 명확한 가이드를 찾지 못했다면 바로 이곳에서 시작하세요!
프로그래밍 관련 글을 쓰는 저는 때때로 전문가인 척하거나 그 반대로 허점을 드러내기 쉽습니다. 솔직히 말해서, 처음으로 풀스택 프레임워크인 라라벨을 배우는 과정은 저에게 매우 어려웠습니다. 제가 직장에서 사용하지 않고, 순수한 호기심으로 시작했기 때문입니다. 그래서 저는 라라벨을 시도하고, 결론을 내리고, 혼란스러워하고, 포기하고, 다시 잊어버리기를 반복했습니다. 5~6번 정도 시도한 후에야 비로소 라라벨이 조금씩 이해되기 시작했습니다. (물론, 설명서는 크게 도움이 되지 않았습니다.)
특히 이해하기 어려웠던 부분은 Eloquent였습니다. 정확히 말하면 모델 간의 관계였습니다. Eloquent 자체는 학습해야 할 내용이 너무 많아서 더 힘들었습니다. 실제 프로젝트는 모델링 예제보다 훨씬 더 복잡하기 때문에, 모델링 예시나 블로그 게시물들은 현실과 동떨어져 보였습니다. 안타깝게도, 공식 문서에서도 비슷한 예제를 반복하거나, 유용한 자료를 찾더라도 설명이 너무 부족하거나 누락된 부분이 많아서 도움이 되지 않았습니다.
(참고로, 저는 이전에 공식 문서를 비판했다는 이유로 공격받은 적이 있습니다. 만약 비슷한 생각을 하신다면, 제 표준 답변을 참고해주세요. Django 문서를 먼저 확인하신 후, 다시 이야기합시다.)
결국, 조금씩 개념이 잡히면서 의미를 이해하게 되었습니다. 마침내 프로젝트를 제대로 모델링하고 모델을 편안하게 사용할 수 있게 되었습니다. 그러던 어느 날, 저는 이러한 작업을 더욱 즐겁게 만들어주는 깔끔한 컬렉션 트릭들을 발견했습니다. 이 글에서는 가장 기본적인 내용부터 시작하여 실제 프로젝트에서 마주칠 수 있는 모든 사용 사례를 다룰 예정입니다.
Eloquent 모델 관계가 어려운 이유는 무엇일까요?
안타깝게도 많은 라라벨 개발자들이 모델을 제대로 이해하지 못하고 있습니다.
왜 그럴까요?
오늘날, 라라벨 관련 강의, 기사, 비디오는 폭발적으로 증가하고 있지만, 전반적인 이해도는 부족합니다. 이는 중요한 문제이며, 한번쯤 되짚어볼 필요가 있다고 생각합니다.

제 생각에는 Eloquent 모델 관계는 전혀 어렵지 않습니다. 적어도 '어려움'의 정의를 고려했을 때 말입니다. 라이브 스키마 마이그레이션은 어렵습니다. 새로운 템플릿 엔진을 만드는 것도 어렵습니다. 라라벨의 핵심 코드에 기여하는 것 역시 어렵습니다. 하지만 ORM을 배우고 사용하는 것은, 음... 그렇게 어려울 수 없습니다! 🤭🤭
실제로, 라라벨을 처음 접하는 PHP 개발자들이 Eloquent를 어렵게 느끼는 경우가 많습니다. 이것이 근본적인 문제이며, 제 생각에는 여러 요인들이 이 문제에 기여하고 있습니다 ( 다소 비판적이고 인기 없는 의견이라는 점을 감안해주세요!).
- 라라벨 이전에 대부분의 PHP 개발자들은 CodeIgniter 프레임워크를 접했습니다 (여전히 건재하다는 것은 덤이고, 라라벨이나 CakePHP와 더 유사해지고 있습니다.) 이전 CodeIgniter 커뮤니티에서 '모범 사례'로 여겨졌던 것은 SQL 쿼리를 필요한 곳에 직접 삽입하는 것이었습니다. 오늘날 새로운 CodeIgniter가 등장했지만, 기존의 습관은 쉽게 바뀌지 않았습니다. 결과적으로, 라라벨을 배울 때 ORM이라는 개념은 PHP 개발자들에게 완전히 새로운 것이었습니다.
- Yii, CakePHP 등과 같은 다른 프레임워크를 경험한 극소수의 PHP 개발자를 제외하고는, 나머지 개발자들은 주로 코어 PHP 또는 WordPress와 같은 환경에서 작업합니다. 이러한 환경에서는 객체 지향 프로그래밍(OOP) 기반 사고방식이 존재하지 않기 때문에, 프레임워크, 서비스 컨테이너, 디자인 패턴, ORM 등은 외계의 개념처럼 느껴집니다.
- PHP 커뮤니티에는 지속적인 학습에 대한 개념이 거의 없습니다. 대부분의 개발자들은 관계형 데이터베이스를 사용하고, 문자열로 작성된 쿼리를 발행하며, 단일 서버 환경에서 작업하는 것을 선호합니다. 비동기 프로그래밍, 웹 소켓, HTTP 2/3, Linux (Docker는 말할 것도 없고), 단위 테스트, 도메인 기반 디자인 등은 대부분의 PHP 개발자들에게 생소한 개념입니다. 결과적으로, Eloquent를 처음 접했을 때 편안함을 느끼기에는 너무나 새롭고 도전적인 것들로 가득 차 있습니다.
- 데이터베이스와 모델링에 대한 전반적인 이해도 부족합니다. 데이터베이스 설계는 Eloquent 모델과 직접적으로 연결되어 있기 때문에, Eloquent를 더욱 어렵게 느껴지게 만듭니다.
제가 전 세계 모든 PHP 개발자를 비난하거나 일반화하려는 것은 아닙니다. 뛰어난 PHP 개발자들도 많으며, 그중 많은 수는 훌륭한 실력을 가지고 있지만, 전체 비율은 매우 낮다는 점이 아쉬울 뿐입니다.
이 글을 읽고 계신다면, 이 모든 장벽을 넘어 라라벨을 접하고 Eloquent로 고생하고 있다는 의미입니다.
축하합니다! 👏
거의 다 왔습니다. 모든 준비가 끝났고, 올바른 순서와 세부 사항에 집중하기만 하면 됩니다. 즉, 데이터베이스 수준에서 시작해보겠습니다.
데이터베이스 모델: 관계 및 카디널리티
쉽게 설명하기 위해, 이 글에서는 관계형 데이터베이스를 사용한다고 가정하겠습니다. ORM은 원래 관계형 데이터베이스를 위해 개발되었으며, RDBMS가 여전히 압도적인 인기를 누리고 있기 때문입니다.
데이터 모델
먼저 데이터 모델을 더 잘 이해해 봅시다. 모델 (또는 더 정확하게 말하면 데이터 모델)의 아이디어는 데이터베이스에서 나왔습니다. 데이터베이스, 데이터, 데이터 모델은 모두 연결되어 있습니다. 데이터 모델이란 무엇일까요? 간단히 말해서, 데이터를 저장하고 구성하기로 결정한 방법입니다. 예를 들어, 전자상거래 쇼핑몰에서는 모든 데이터를 하나의 거대한 테이블에 저장할 수 있습니다 (끔찍한 방법이지만, 안타깝게도 PHP 세계에서는 드문 일이 아닙니다). 이것이 바로 데이터 모델입니다. 또는 20개의 기본 테이블과 16개의 연결 테이블로 데이터를 나눌 수도 있습니다. 이것도 데이터 모델입니다.
데이터베이스에서 데이터가 구성되는 방식이 프레임워크의 ORM에서 배열되는 방식과 반드시 100% 일치할 필요는 없습니다. 하지만 개발할 때는 가능한 한 가깝게 유지하려고 노력하는 것이 좋습니다. 그래야만 개발자가 신경 써야 할 사항이 하나 더 늘어나지 않기 때문입니다.
카디널리티
카디널리티라는 용어도 빠르게 알아봅시다. 간단히 말하면, '개수'를 의미합니다. 1, 2, 3... 모두 어떤 것의 카디널리티가 될 수 있습니다. 끝입니다! 이제 계속 진행합시다!
관계
이제 데이터를 모든 종류의 시스템에 저장할 때마다 데이터 포인트들이 서로 연관될 수 있는 방법이 있습니다. 추상적이고 지루하게 들릴 수 있지만 조금만 더 들어주세요. 서로 다른 데이터 항목들이 연결되는 방식을 관계라고 합니다. 아이디어를 완전히 이해했는지 확인하기 위해, 데이터베이스가 아닌 몇 가지 예시를 먼저 살펴보겠습니다.
- 배열에 모든 데이터를 저장한다면, 가능한 관계 중 하나는 다음 데이터 항목이 이전 인덱스보다 1만큼 큰 인덱스에 위치한다는 것입니다.
- 데이터를 이진 트리에 저장하는 경우, 가능한 관계 중 하나는 왼쪽 자식 트리가 항상 부모 노드보다 작은 값을 가진다는 것입니다 (트리를 그런 식으로 유지하도록 선택한 경우).
- 데이터를 동일한 길이의 배열 배열로 저장하면 행렬을 모방할 수 있으며, 그 속성은 데이터의 관계가 됩니다.
따라서 데이터 맥락에서 '관계'라는 단어는 고정된 의미를 가지지 않습니다. 실제로, 두 사람이 같은 데이터를 보고 있다면, 두 가지 매우 다른 데이터 관계 (통계학에서 자주 볼 수 있습니다!)를 식별할 수 있으며, 둘 다 유효할 수 있습니다.
관계형 데이터베이스
지금까지 논의한 모든 내용을 바탕으로, 드디어 웹 프레임워크 (라라벨)의 모델과 직접적으로 연결되는 관계형 데이터베이스에 대해 이야기할 수 있게 되었습니다. 우리 대부분이 사용하는 기본적인 데이터베이스는 MySQL, MariaDB, PostgreSQL, MSSQL, SQL Server, SQLite 등입니다. 우리는 이것들을 RDBMS라고 부른다는 것을 알고 있지만, 그 의미와 중요성에 대해서는 잊어버린 경우가 많습니다.
RDBMS의 'R'은 관계형 (Relational)을 의미합니다. 이것은 임의로 선택된 단어가 아닙니다. 이 용어는 이러한 데이터베이스 시스템이 저장된 데이터 간의 관계와 효율적으로 작동하도록 설계되었다는 점을 강조합니다. 사실, 여기서 '관계'는 엄격한 수학적 의미를 지니고 있습니다. 개발자들이 그것에 대해 신경 쓸 필요는 없지만, 이러한 유형의 데이터베이스의 기본 원리가 엄격한 수학적 의미를 가진다는 것을 알아두는 것은 도움이 됩니다.
SQL과 NoSQL에 대해 더 알아보려면 다음 자료를 참고하세요.
경험을 통해 RDBMS의 데이터는 테이블로 저장된다는 것을 알고 있습니다. 그렇다면 관계는 어디에 있을까요?
RDBMS의 관계 유형
이 부분은 라라벨과 모델 관계 전체 주제에서 아마도 가장 중요한 부분일 것입니다. 만약 이 부분을 이해하지 못한다면, Eloquent는 결코 이해되지 않을 것입니다. 따라서 다음 몇 분 동안 집중해주십시오 (그렇게 어렵지 않습니다).
RDBMS를 사용하면 데이터베이스 수준에서 데이터 간의 관계를 정의할 수 있습니다. 즉, 이러한 관계는 비실용적이거나 주관적이지 않으며, 다른 사람들도 같은 결과로 생성하거나 추론할 수 있습니다.
또한 RDBMS에는 이러한 관계를 만들고 적용할 수 있는 다음과 같은 특정 기능/도구가 있습니다.
- 기본 키
- 외래 키
- 제약 조건
이 글이 데이터베이스 강의가 되기를 원하지 않으므로, 이러한 개념에 대해 이미 알고 있다고 가정하겠습니다. 만약 그렇지 않거나 자신감이 부족하다면, 이 친절한 동영상을 추천합니다 (전체 시리즈를 자유롭게 살펴보세요).

흥미롭게도, 이러한 RDBMS 스타일의 관계는 실제 애플리케이션에서 가장 흔하게 발생하는 관계이기도 합니다 (소셜 네트워크는 테이블 모음이 아닌 그래프로 가장 잘 모델링되므로 항상 그런 것은 아니지만). 이제 하나씩 살펴보고, 각각이 어떻게 유용할 수 있는지 알아보겠습니다.
일대일 관계
거의 모든 웹 애플리케이션에는 사용자 계정이 있습니다. 사용자와 계정에 대해 일반적으로 사실인 내용은 다음과 같습니다.
- 사용자는 하나의 계정만 가질 수 있습니다.
- 계정은 한 명의 사용자만 소유할 수 있습니다.
물론, 한 사람이 다른 이메일로 가입하여 두 개의 계정을 만들 수 있다고 주장할 수 있습니다. 하지만 웹 애플리케이션의 관점에서 볼 때, 두 사람은 서로 다른 계정을 가진 서로 다른 두 사람입니다. 예를 들어, 애플리케이션은 한 계정의 데이터를 다른 계정에 표시하지 않습니다.
이 모든 세밀한 구분의 의미는, 애플리케이션에서 이러한 상황이 발생하고 관계형 데이터베이스를 사용하고 있다면, 일대일 관계로 설계해야 한다는 것입니다. 다시 말하지만, 아무도 인위적으로 강요하지 않습니다. 비즈니스 도메인에 명확한 상황이 있고, 여러분이 우연히 관계형 데이터베이스를 사용하고 있는 것입니다. 이 두 가지 조건이 모두 충족되어야만 일대일 관계가 필요하다는 결론에 도달할 수 있습니다.
사용자와 계정 예시의 경우, 스키마를 만들 때 이 관계를 구현할 수 있는 방법은 다음과 같습니다.
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE accounts(
id INT NOT NULL AUTO_INCREMENT,
role VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(id) REFERENCES users(id)
);
여기서 핵심 트릭을 알아채셨나요? 일반적으로 애플리케이션을 만들 때 매우 드문 일이지만, accounts 테이블의 id 필드는 기본 키와 외래 키로 동시에 설정되어 있습니다! 외래 키 속성은 해당 필드를 users 테이블에 연결하는 반면 (당연히 🙄), 기본 키 속성은 id 열을 고유하게 만듭니다. 이것이 바로 진정한 일대일 관계입니다!
물론, 이 관계의 완전성은 보장되지 않습니다. 예를 들어, 계정 테이블에 항목을 추가하지 않고 200명의 새로운 사용자를 추가하는 것을 막을 수는 없습니다. 이렇게 하면 1:0 관계가 됩니다! 🤭🤭 하지만 순수한 구조의 범위 내에서는 이것이 최선입니다. 계정 없이 사용자를 추가하지 않으려면, 데이터베이스 트리거 또는 라라벨에서 시행되는 유효성 검사와 같은 프로그래밍적 논리를 활용해야 합니다.

만약 스트레스를 받기 시작했다면, 아주 좋은 조언이 있습니다.
- 천천히 하세요. 필요한 만큼 천천히 하세요. 이 글과 오늘 즐겨찾기에 추가한 15개의 다른 글을 모두 읽으려고 하지 말고, 이 글에 집중하세요. 3일, 4일, 5일이 걸리더라도 괜찮습니다. 목표는 Eloquent 모델 관계를 완전히 이해하고 더 이상 고민하지 않는 것입니다. 이전에는 여러 글을 돌아다니며 수백 시간을 허비했지만 아무런 도움이 되지 않았습니다. 그러니 이번에는 다른 방법을 사용해보세요. 😇
- 이 글은 라라벨 Eloquent에 관한 글이지만, 라라벨 이야기는 나중에 나올 것입니다. 모든 것의 기초는 데이터베이스 스키마이므로, 우선 데이터베이스 스키마를 올바르게 만드는 데 집중해야 합니다. 데이터베이스 수준에서만 작업할 수 없다면 (프레임워크가 없다고 가정하고) 모델과 관계를 완전히 이해할 수 없을 것입니다. 따라서 지금은 라라벨을 잊어버리세요. 완전히 말입니다. 지금은 데이터베이스 설계에 대해서만 이야기하고 작업하고 있습니다. 때때로 라라벨 언급이 있겠지만, 만약 그러한 언급이 그림을 복잡하게 만든다면 무시해주세요.
- 나중에 데이터베이스와 그 기능에 대해 좀 더 자세히 알아보세요. MongoDB의 인덱스, 성능, 트리거, 기본 데이터 구조, 캐싱, 관계 등에 대해 학습해두면 엔지니어로서 큰 도움이 될 것입니다. 프레임워크 모델은 겉껍데기에 불과하다는 것을 기억하세요. 플랫폼의 실제 기능은 기본 데이터베이스에서 나옵니다.
일대다 관계
이미 눈치채셨을 수도 있지만, 일대다 관계는 우리 모두가 일상 업무에서 직관적으로 만드는 관계입니다. 예를 들어, 사용자 테이블에 외래 키를 저장하기 위해 orders 테이블 (가상 예시)을 만들 때, 사용자 간에 일대다 관계를 만듭니다. 왜 그럴까요? 누가 얼마나 많이 가질 수 있는지 다시 한번 생각해봅시다. 한 사용자는 하나 이상의 주문을 가질 수 있습니다. 이것은 거의 모든 전자상거래 시스템이 작동하는 방식입니다. 그리고 반대로 생각했을 때, 관계는 주문이 한 사용자에게만 속할 수 있다고 말합니다. 이것도 매우 합리적입니다.
데이터 모델링, RDBMS 서적 및 시스템 문서에서 이러한 상황은 일반적으로 다음 다이어그램으로 표현됩니다.

삼지창처럼 생긴 세 개의 선이 보이시나요? 이것은 '다수'를 나타내는 기호이므로, 이 다이어그램은 한 사용자가 여러 주문을 가질 수 있음을 나타냅니다.
우리가 반복적으로 접하는 '다수'와 '일'의 수는 관계의 카디널리티라고 합니다 (이전 섹션에서 이 단어를 기억하시나요?). 이 글에서는 이 용어가 많이 사용되지 않겠지만, 면접이나 추가 학습 중에 이 용어를 접하게 될 경우 개념을 알고 있으면 도움이 될 것입니다.
간단하죠? 그리고 실제 SQL 관점에서 이 관계를 생성하는 것 또한 간단합니다. 사실, 일대일 관계보다 훨씬 간단합니다!
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE orders(
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
description VARCHAR(50) NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(user_id) REFERENCES users(id)
);
orders 테이블은 각 주문에 대한 사용자 ID를 저장합니다. orders 테이블의 user_id가 고유해야 한다는 제약 조건은 없으므로, 동일한 ID를 여러 번 반복할 수 있습니다. 이것이 일대다 관계를 만드는 방법이며, 그 아래에 숨겨진 신비한 마법은 없습니다. user_id는 orders 테이블에 단순한 방식으로 저장될 뿐이며, SQL에는 일대다, 일대일과 같은 개념이 없습니다. 하지만 이렇게 데이터를 저장하면 일대다 관계가 있다고 생각할 수 있습니다.

이제 이해가 되시기를 바랍니다. 아니면, 적어도 이전보다 더 이해가 되시기를 바랍니다. 😅 다른 모든 것들과 마찬가지로, 이것은 단순한 연습 문제일 뿐이며, 실제 상황에서 4~5번 정도 해보면 더 이상 생각조차 하지 않게 될 것입니다.
다대다 관계
실제로 발생할 수 있는 다음 유형의 관계는 다대다 관계입니다. 다시 한번, 프레임워크에 대해 걱정하거나 데이터베이스에 바로 뛰어들기 전에, 실제 사례인 책과 저자를 생각해봅시다. 여러분이 좋아하는 작가를 떠올려보세요. 그 작가는 한 권 이상의 책을 썼겠죠? 동시에, 여러 작가가 한 권의 책을 공동으로 작업하는 것을 보는 것도 흔한 일입니다 (적어도 논픽션 장르에서는). 따라서 한 작가는 여러 권의 책을 쓸 수 있고, 여러 작가는 한 권의 책을 쓸 수 있습니다. 이 두 개체 (책과 저자) 사이에서 다대다 관계가 형성됩니다.
도서관이나 책, 저자가 포함된 실제 앱을 만들 가능성은 거의 없다는 것을 감안하여, 몇 가지 예시를 더 살펴보겠습니다. B2B 환경에서 제조업체는 공급업체로부터 품목을 주문하고 송장을 받습니다. 송장에는 여러 품목이 포함되며, 각 품목에는 공급된 수량과 품목이 나열됩니다. 예를 들어, 5인치 파이프 200개 등입니다. 이러한 상황에서 품목과 송장은 다대다 관계를 가집니다 (한번 생각해보고 스스로 확신해보세요). 차량 관리 시스템에서 차량과 운전자는 비슷한 관계를 갖게 됩니다. 전자상거래 사이트에서 즐겨찾기 또는 위시리스트와 같은 기능을 고려하면, 사용자와 제품은 다대다 관계를 가질 수 있습니다.
이제 충분합니다. SQL에서 이 다대다 관계를 만드는 방법은 무엇일까요? 일대다 관계가 작동하는 방식에 대한 지식을 바탕으로, 두 테이블의 다른 테이블에 대한 외래 키를 저장해야 한다고 생각할 수 있습니다. 하지만 이렇게 하려고 하면 큰 문제에 직면하게 됩니다. 책의 저자가 다대다 관계를 가져야 하는 예시를 살펴봅시다.

언뜻 보기에는 모든 것이 괜찮아 보입니다. 책은 다대다 방식으로 저자에게 정확하게 매핑됩니다. 하지만 authors 테이블의 데이터를 자세히 살펴보세요. 책 ID 12와 13은 모두 Peter M. (저자 ID 2)이 작성했기 때문에 항목을 반복할 수밖에 없습니다. 이제 authors 테이블에 데이터 무결성 문제가 있을 뿐만 아니라 (적절한 정규화 등등), id 열의 값이 이제 반복됩니다. 이는 우리가 선택한 디자인에서 기본 키 열이 없을 수 있다는 것을 의미합니다. (기본 키는 중복된 값을 가질 수 없으므로) 결국 모든 것이 무너집니다.

분명히, 이를 위한 새로운 방법이 필요하며, 다행히도 이 문제는 이미 해결되었습니다. 외래 키를 두 테이블에 직접 저장하면 문제가 발생하므로, RDBMS에서 다대다 관계를 만드는 올바른 방법은 '조인 테이블'이라고 불리는 테이블을 만드는 것입니다. 기본적인 아이디어는 두 개의 원본 테이블을 그대로 두고, 다대다 매핑을 표시하기 위해 세 번째 테이블을 만드는 것입니다.
실패했던 예시를 조인 테이블을 포함하여 다시 실행해보겠습니다.

급격한 변화가 있었습니다.
- authors 테이블의 열 수가 줄었습니다.
- books 테이블의 열 수도 줄었습니다.
- 더 이상 반복할 필요가 없으므로, authors 테이블의 행 수가 줄었습니다.
- authors_books라는 새로운 테이블이 생겼는데, 이 테이블에는 저자 ID가 어떤 책 ID와 연결되어 있는지에 대한 정보가 있습니다. 조인 테이블의 이름은 무엇이든 지정할 수 있지만, 관례에 따라 단순히 밑줄을 사용하여 테이블을 나타내는 두 테이블을 조인한 결과입니다.
조인 테이블에는 기본 키가 없으며, 대부분의 경우 두 테이블의 ID인 두 개의 열만 포함되어 있습니다. 이전 예시에서 외래 키 열을 제거하고 이 새 테이블에 붙여넣은 것과 거의 같습니다. 기본 키가 없으므로, 모든 관계를 기록하는 데 필요한 만큼 반복할 수 있습니다.
이제 조인 테이블이 관계를 명확하게 표시하는 방법을 눈으로 볼 수 있지만, 애플리케이션에서 어떻게 액세스할 수 있을까요? 비밀은 바로 '조인 테이블'이라는 이름에 있습니다. SQL 쿼리 관련 강의는 아니므로 깊이 들어가지는 않겠지만, 특정 저자의 모든 책을 하나의 효율적인 쿼리로 가져오고 싶다면 테이블을 SQL 조인해야 합니다. authors, authors_books, books 테이블을 모두 조인해야 하며, authors 및 authors_books 테이블은 id 및 author_id 열을 각각 조인하고, authors_books 및 books 테이블은 각각 book_id 및 id 열을 조인합니다.

네, 힘드네요. 하지만 긍정적인 부분을 봅시다. Eloquent 모델을 다루기 전에 필요한 모든 이론/기초 작업을 완료했습니다. 이 모든 것이 선택 사항이 아니라는 것을 상기시켜 드립니다! 데이터베이스 설계를 모르면 영원히 Eloquent의 혼란 속에서 헤매게 될 것입니다. 또한, Eloquent가 무엇을 하든, 무엇을 하려고 하든, 데이터베이스 수준의 세부 사항을 완벽하게 반영하므로, RDBMS에서 도망치면서 Eloquent를 배우려는 시도가 헛수고인 이유를 쉽게 알 수 있습니다.
라라벨 Eloquent에서 모델 관계 생성하기
드디어, 7만 마일 정도 돌아온 후에 Eloquent와 모델, 모델을 만들고 사용하는 방법에 대해 이야기할 수 있는 단계에 도달했습니다. 이전 섹션에서, 모든 것은 데이터베이스와 데이터를 모델링하는 방법에서 시작된다는 것을 배웠습니다. 저는 새로운 프로젝트를 시작하는 예시를 사용해야 한다는 것을 깨달았습니다. 동시에 이 예시가 블로그와 저자, 책과 서가 (실제로도 사용되지만 너무 많이 사용되었습니다) 에 관한 것이 아닌, 실제 세계에서 사용되는 것이기를 바랍니다.
부드러운 장난감을 판매하는 상점을 상상해 봅시다. 또한 시스템에서 사용자, 주문, 송장, 품목, 카테고리, 하위 카테고리 및 트랜잭션의 7가지 항목을 식별할 수 있는 요구 사항 문서를 받았다고 가정합시다. 물론, 더 복잡할 수도 있지만 문제는 제쳐두고, 문서에서 앱으로 이동하는 방법에 집중해 보겠습니다.
시스템의 주요 항목을 식별했다면, 지금까지 논의했던 데이터베이스 관계 측면에서 이러한 항목이 서로 어떻게 연결되어 있는지 생각해봐야 합니다. 제가 생각할 수 있는 것은 다음과 같습니다.
- 사용자 및 주문: 일대다 관계.
- 주문 및 송장: 일대일 관계. 이 관계가 항상 그런 것은 아니며 비즈니스 도메인에 따라 일대다, 다대일 또는 다대다 관계가 있을 수 있다는 것을 알고 있습니다. 하지만 대부분의 소규모 전자상거래 쇼핑몰의 경우, 하나의 주문은 하나의 송장을 생성하고 그 반대의 경우도 마찬가지입니다.
- 주문 및 품목: 다대다 관계.
- 품목 및 카테고리: 다대일 관계. 다시 말하지만, 대규모 전자상거래 사이트에서는 그렇지 않겠지만, 여기서는 소규모 운영을 고려합니다.
- 카테고리 및 하위 카테고리: 일대다 관계. 다시 말하지만, 이 관계와 모순되는 실제 사례를 많이 찾을 수 있겠지만, Eloquent가 이미 충분히 어렵기 때문에 데이터 모델링을 더욱 어렵게 만들지는 않겠습니다!
- 주문 및 거래: 일대다 관계. 제 선택에 대한 정당성을 위해 다음 두 가지를 추가하고 싶습니다. 1) 트랜잭션과 송장 간의 관계를 추가할 수도 있었지만, 이는 데이터 모델링 결정일 뿐입니다. 2) 여기에서 일대다인 이유는 무엇일까요? 글쎄요, 주문 결제가 어떤 이유로 실패하고 다음에 성공하는 것이 일반적입니다. 이 경우 해당 주문에 대해 생성된 두 개의 트랜잭션이 있습니다. 실패한 트랜잭션을 표시할지 여부는 비즈니스 결정이지만, 중요한 데이터를 캡처하는 것이 좋습니다.
다른 관계가 있을까요? 더 많은 관계가 가능하지만 실용적이지 않습니다. 예를 들어, 사용자가 많은 트랜잭션을 가지고 있으므로 그 사이에 관계가 있어야 한다고 말할 수 있습니다. 여기서 깨달아야 할 점은 이미 간접적인 관계가 있다는 것입니다. 사용자 -> 주문 -> 거래, 그리고 일반적으로 RDBMS는 테이블 조인을 매우 잘 처리하기 때문에 이 정도면 충분합니다. 둘째, 이 관계를 생성한다는 것은 transactions 테이블에 user_id 열을 추가하는 것을 의미합니다. 가능한 모든 직접적인 관계에 대해 이렇게 하면 데이터베이스에 훨씬 더 많은 부하가 걸리고 (특히 UUID를 사용하고 인덱스를 유지 관리하는 경우 더 많은 스토리지 형태로) 전체 시스템이 연결됩니다. 물론, 비즈니스에서 트랜잭션 데이터가 필요하다고 말하고 1.5초 이내에 필요하다고 하면 해당 관계를 추가하여 작업 속도를 높일 수 있습니다(trade-offs, trade-offs...).
이제 드디어 코드를 작성할 시간입니다!

라라벨 모델 관계 — 실제 코드 예시
이 글의 다음 단계는 실제로 코드를 작성해보는 것입니다. 이전 전자상거래 예시와 동일한 데이터베이스 개체를 사용하고, 라라벨을 설치한 직후 라라벨의 모델이 어떻게 생성되고 연결되는지 살펴보겠습니다.
당연히 개발 환경이 설정되어 있고 종속성 관리를 위해 Composer를 설치하고 사용할 줄 안다고 가정합니다.
$ composer global require laravel/installer -W $ laravel new model-relationships-study
이 두 개의 콘솔 명령어는 라라벨 설치 프로그램을 설치합니다 (이전 버전이 설치되어 있다면 업그레이드에 -W 파트가 사용됨). 궁금하신 분들을 위해 말씀드리자면, 글을 쓰고 있는 시점의 라라벨 버전은 8.5.9입니다. 당황하고 업그레이드해야 할까요? 저희 애플리케이션의 맥락에서는 라라벨 5와 라라벨 8 사이에 큰 변화가 없을 것으로 예상되므로, 이에 대해서는 권장하지 않습니다. 몇 가지 사항이 변경되어 이 글에 영향을 줄 수 있지만 (예: 모델 팩토리), 코드를 이식할 수 있을 것입니다.
이미 데이터 모델과 관계를 생각했으므로, 모델을 생성하는 부분은 사소할 것입니다. 그리고 데이터베이스 스키마에 100% 의존하므로, 데이터베이스 스키마를 미러링하는 방법도 볼 수 있습니다 (지금은 망가진 레코드처럼 들립니다!).
즉, 데이터베이스에 적용할 모든 모델에 대한 마이그레이션 (및 모델 파일)을 먼저 생성해야 합니다. 나중에 모델을 작업하고 관계를 추가할 수 있습니다.
그렇다면 어떤 모델부터 시작해야 할까요? 물론, 가장 단순하고 연결이 가장 적은 것부터 시작해야 합니다. 이 경우에는 사용자 모델을 의미합니다. 라라벨은 이 모델과 함께 제공되므로 (그리고 이 모델 없이는 작동할 수 없으므로 🤣), 마이그레이션 파일을 수정하고 우리의 간단한 필요에 맞게 모델을 정리해 봅시다.
마이그레이션 클래스는 다음과 같습니다.
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
}
실제 프로젝트를 만들려는 것이 아니므로 암호, is_active 등을 입력할 필요가 없습니다. users 테이블에는 id와 사용자 이름이라는 두 개의 열만 있습니다.
다음으로 카테고리에 대한 마이그레이션을 생성하겠습니다. 라라벨은 단일 명령어로도 모델을 생성할 수 있는 편리함을 제공하므로, 지금은 모델 파일을 다루지 않겠지만, 이점을 활용해 보겠습니다.
$ php artisan make:model Category -m Model created successfully. Created Migration: 2021_01_26_093326_create_categories_table
마이그레이션 클래스는 다음과 같습니다.
class CreateCategoriesTable extends Migration
{
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
});
}
}
down() 함수가 없다는 사실에 놀라지 마십시오. 실제로 열이나 테이블을 삭제하거나 열 유형을 변경하면 복구할 수 없는 데이터 손실이 발생하므로, 거의 사용하지 않습니다. 개발 과정에서 전체 데이터베이스를 삭제한 다음 마이그레이션을 다시 실행하는 것이 일반적입니다. 하지만 여기서 너무 벗어났으므로, 다시 돌아와서 다음 항목을 다뤄보겠습니다. 하위 카테고리는 카테고리와 직결되어 있으므로, 다음 순서로 진행하는 것이 좋을 것 같습니다.
$ php artisan make:model SubCategory -m Model created successfully. Created Migration: 2021_01_26_140845_create_sub_categories_table
자, 이제