Drizzle 관계의 유일한 목적은 여러분이 관계형 데이터를 가장 간단하고 명확하게 쿼리할 수 있도록 하는 것입니다.
관계형 쿼리
조인을 사용한 쿼리
일대일 관계
Drizzle ORM은 relations 연산자를 사용하여 테이블 간의 일대일 관계를 정의할 수 있는 API를 제공합니다.
다음은 사용자와 사용자 간의 일대일 관계 예제입니다. 한 사용자가 다른 사용자를 초대할 수 있는 경우를 보여줍니다. 이 예제는 자기 참조(self reference)를 사용합니다.
또 다른 예제로, 사용자의 프로필 정보를 별도의 테이블에 저장하는 경우를 살펴보겠습니다. 이 경우 외래 키가 profile_info 테이블에 저장되므로, user 관계에는 fields나 references가 없습니다. 이는 TypeScript에게 user.profileInfo가 null일 수 있음을 알려줍니다.
이 예제에서 user.profileInfo는 null일 수 있음을 TypeScript가 인식합니다. 이는 외래 키가 profile_info 테이블에 있기 때문입니다.
일대다(One-to-many) 관계
Drizzle ORM은 relations 연산자를 사용하여 테이블 간의 일대다 관계를 정의할 수 있는 API를 제공합니다.
예를 들어, 사용자와 그들이 작성한 게시물 간의 일대다 관계를 정의해 보겠습니다.
이제 게시물에 댓글을 추가해 보겠습니다.
이 코드는 사용자, 게시물, 댓글 간의 관계를 정의합니다. 사용자는 여러 게시물을 작성할 수 있고, 각 게시물은 여러 댓글을 가질 수 있습니다. 이러한 관계는 Drizzle ORM의 relations 연산자를 통해 쉽게 정의할 수 있습니다.
다대다 관계
Drizzle ORM은 junction 또는 join 테이블을 통해 테이블 간의 다대다 관계를 정의할 수 있는 API를 제공합니다. 이 테이블은 명시적으로 정의되어야 하며, 관련된 테이블 간의 연결을 저장합니다.
사용자와 그룹 간의 다대다 관계 예제:
이 예제에서는 users와 groups 테이블 간의 다대다 관계를 정의하고, 이를 연결하는 usersToGroups 테이블을 생성합니다. usersToGroups 테이블은 userId와 groupId를 외래 키로 사용하여 두 테이블 간의 관계를 관리합니다.
외래 키(Foreign Keys)
relations가 외래 키와 비슷해 보인다는 것을 눈치챘을 겁니다. 심지어 references 속성도 가지고 있죠. 그렇다면 둘의 차이는 무엇일까요?
외래 키도 비슷한 목적을 가지고 있지만, 테이블 간의 관계를 정의하는 방식에서 relations와는 다른 수준에서 동작합니다.
외래 키는 데이터베이스 수준의 제약 조건입니다. 모든 insert, update, delete 작업에서 검사되며, 제약 조건이 위반되면 오류를 발생시킵니다.
반면에 **relations**는 더 높은 수준의 추상화입니다. 애플리케이션 수준에서만 테이블 간의 관계를 정의하는 데 사용됩니다. relations는 데이터베이스 스키마에 영향을 미치지 않으며, 암묵적으로 외래 키를 생성하지도 않습니다.
이것이 의미하는 바는, relations와 외래 키를 함께 사용할 수 있지만, 서로 의존하지 않는다는 것입니다.
외래 키를 사용하지 않고 relations를 정의할 수 있고(그 반대도 가능), 이는 외래 키를 지원하지 않는 데이터베이스에서도 사용할 수 있게 해줍니다.
다음 두 예제는 Drizzle의 관계형 쿼리를 사용하여 데이터를 조회할 때 동일하게 동작합니다.
부모 테이블의 참조된 데이터가 수정될 때 발생할 동작을 지정할 수 있습니다. 이러한 동작을 “외래 키 동작”이라고 합니다. PostgreSQL은 이러한 동작에 대해 여러 옵션을 제공합니다.
삭제/업데이트 시 동작
CASCADE: 부모 테이블의 행이 삭제되면, 자식 테이블의 모든 관련 행도 함께 삭제됩니다. 이는 자식 테이블에 고아(orphaned) 행이 남지 않도록 보장합니다.
NO ACTION: 기본 동작입니다. 자식 테이블에 관련 행이 있는 경우, 부모 테이블의 행 삭제를 방지합니다. 부모 테이블의 DELETE 작업이 실패합니다.
RESTRICT: NO ACTION과 유사합니다. 자식 테이블에 종속된 행이 있는 경우, 부모 테이블의 행 삭제를 방지합니다. NO ACTION과 동일하며, 호환성을 위해 포함되었습니다.
SET DEFAULT: 부모 테이블의 행이 삭제되면, 자식 테이블의 외래 키 컬럼이 기본값으로 설정됩니다. 기본값이 없는 경우, DELETE 작업이 실패합니다.
SET NULL: 부모 테이블의 행이 삭제되면, 자식 테이블의 외래 키 컬럼이 NULL로 설정됩니다. 이 동작은 자식 테이블의 외래 키 컬럼이 NULL 값을 허용한다고 가정합니다.
ON DELETE와 유사하게, 참조된 컬럼이 변경(업데이트)될 때 호출되는 ON UPDATE도 있습니다. 가능한 동작은 동일하지만, SET NULL과 SET DEFAULT의 경우 컬럼 목록을 지정할 수 없습니다. 이 경우, CASCADE는 참조된 컬럼의 업데이트된 값이 참조하는 행에 복사됨을 의미합니다.
Drizzle에서는 references()의 두 번째 인자를 사용하여 외래 키 동작을 추가할 수 있습니다.
동작 타입
다음 예제에서 posts 스키마의 author 필드에 onDelete: 'cascade'를 추가하면, user를 삭제할 때 관련된 모든 Post 레코드도 함께 삭제됩니다.
foreignKey 연산자로 지정된 제약 조건의 경우, 외래 키 동작은 다음과 같은 구문으로 정의됩니다.
관계 명확히 하기
Drizzle은 동일한 두 테이블 간에 여러 관계를 정의할 때, 이를 명확히 구분하기 위해 relationName 옵션을 제공합니다. 예를 들어, author와 reviewer 관계를 모두 갖는 posts 테이블을 정의하는 경우를 생각해볼 수 있습니다.
위 예제에서 relationName을 사용하여 author와 reviewer 관계를 명확히 구분하고 있습니다. 이를 통해 동일한 두 테이블 간에 여러 관계를 정의할 때 발생할 수 있는 혼란을 방지할 수 있습니다.