아래 내용은 첨부된 이미지를 토대로 Isar의 Links(관계 설정) 기능에 대해 최대한 빠짐없이, 그리고 개발 초보자 분들도 이해하기 쉽게 정리한 것입니다. 코드 예시와 함께 상세한 설명을 덧붙였으니 참고해 주세요.
Links는 말 그대로 서로 다른 객체 간의 ‘관계’를 표현하기 위한 도구입니다.
예를 들어,
Comment)이 작성자(User)를 가지는 경우, Student)이 특정 선생님(Teacher)을 가지는 경우, Post)이 여러 태그(Tag)를 가지는 경우 등 이처럼 객체 간 서로 연결되어야 할 때, 기존의 데이터베이스에서는 외래 키(Foreign Key) 등을 활용합니다. 하지만 Isar에서는 클래스 내에 IsarLink 혹은 IsarLinks라는 특수한 변수를 두어 직관적이고 간단하게 관계를 구성할 수 있습니다.
관계(Relationship)란?
두 개 이상의 테이블(또는 객체) 사이에서 “어떤 자료가 서로 어떻게 연결되어 있는지”를 나타내는 것.
- 1:1 관계: 한 객체가 정확히 하나의 객체와만 연결(예: 주민 - 주민등록증)
- 1:N 관계: 한 객체가 여러 객체와 연결되거나, 반대로 여러 객체가 한 객체에 연결(예: 선생님 - 학생, 게시글 - 댓글)
- M:N 관계: 여러 객체가 여러 객체와 연결(예: 게시글 - 태그)
IsarLink<T>와 IsarLinks<T>는 Isar에서 관계를 표현하기 위한 핵심 클래스입니다.
IsarLink<T>: 단일 객체와 연결(1:1 관계를 표현하거나, “다(N)” 쪽에서 “1” 쪽 객체를 가리킬 때)IsarLinks<T>: 여러 객체와 연결(1:N, M:N 관계에서 “1”쪽이나 “N”쪽에 여러 객체를 보관할 때)쉽게 말해,
IsarLink는 “하나”를 가리킨다고 이해하면 되고, IsarLinks는 “여러 개”를 가리킨다고 생각하면 됩니다.Foreign Key(외래 키)란?
전통적인 SQL 데이터베이스에서 사용되는 용어로, 한 테이블에서 다른 테이블의 주 키(Primary Key)를 참조하여 관계를 연결하는 방법입니다. Isar에서는IsarLink또는IsarLinks를 사용하여 외래 키를 직접 관리하지 않아도 됩니다.
다음 예시에서는 선생님과 학생의 관계가 1:N(한 명의 선생님은 여러 명의 학생을 가질 수 있고, 한 학생은 정확히 한 명의 선생님만 가진다고 가정)일 때, Isar에서 어떻게 관계를 설정하는지 살펴봅니다.
import 'package:isar/isar.dart';
// 선생님 모델
class Teacher {
// 고유 식별자(Primary Key)
Id id = Isar.autoIncrement;
// 선생님 이름
late String name;
// 여러 명의 학생을 가리킬 수 있는 IsarLinks
final students = IsarLinks<Student>();
}
// 학생 모델
class Student {
// 고유 식별자(Primary Key)
Id id = Isar.autoIncrement;
// 학생 이름
late String name;
// 단일 선생님을 가리키는 IsarLink
final teacher = IsarLink<Teacher>();
}
설명
@collection데코레이터를 달면 Isar가 이 클래스를 데이터베이스 컬렉션으로 인식합니다.Id타입의 변수를 하나 이상 두어야 하는데, 여기서는Isar.autoIncrement값을 이용해 자동 증가하도록 했습니다.Teacher에는 여러Student를 가리키기 위해IsarLinks<Student>()를 사용했습니다.Student에는 하나의Teacher만 가리키면 되므로IsarLink<Teacher>()를 사용했습니다.
void createTeacherAndStudent(Isar isarInstance) async {
// 1) 선생님과 학생 객체를 생성합니다.
final teacher = Teacher()
..name = '홍길동'; // 선생님 이름
final student1 = Student()
..name = '김학생'; // 학생1 이름
final student2 = Student()
..name = '이수강'; // 학생2 이름
// 2) isarInstance.writeTxn을 통해 데이터 저장(트랜잭션) 실행
await isarInstance.writeTxn((isar) async {
// (1) 선생님, 학생 객체를 각각 저장
// 아직 관계(링크)는 연결하지 않았습니다.
final teacherId = await isar.teachers.put(teacher);
final student1Id = await isar.students.put(student1);
final student2Id = await isar.students.put(student2);
// (2) 저장된 객체들을 다시 불러올 수도 있음
// Teacher savedTeacher = await isar.teachers.get(teacherId)!;
// (3) 선생님 - 학생 관계(링크) 연결
// 한 명의 선생님이 여러 명의 학생을 가질 수 있으므로
// teacher.students에 add()로 학생을 추가함
teacher.students.add(student1);
teacher.students.add(student2);
// (4) 반대로 학생 입장에서도 어느 선생님을 가리키는지 설정 가능
// student.teacher.value = teacher; 와 같은 방식
student1.teacher.value = teacher;
student2.teacher.value = teacher;
// (5) 링크 변경 사항도 저장해주어야 함
await teacher.students.save();
await student1.teacher.save();
await student2.teacher.save();
});
}
설명
1.isarInstance.writeTxn((isar) async { ... })를 통해 Isar에 데이터를 쓰는(저장/수정) 작업을 안전하게 수행합니다(트랜잭션).
2.teacher.students.add(student)를 통해 ‘선생님’ 입장에서 학생들과의 관계를 설정하고,student.teacher.value = teacher로 학생 입장에서 선생님을 할당합니다.
3.await teacher.students.save()등을 꼭 호출해야 실제 DB에 링크 정보가 반영됩니다.
IsarLink<T>나 IsarLinks<T>는 내부적으로 관계를 “별도 테이블” 형태로 저장해 둡니다. .save() 해줘야 한다는 점만 기억하면 됩니다.SQL
구조화된 쿼리 언어(Structured Query Language)의 약자. 데이터베이스에서 데이터를 조회, 추가, 수정, 삭제하기 위한 언어입니다. SQL DB에서는 테이블 간 관계를 외래 키(Foreign Key)로 표현합니다.
save() 메서드를 반드시 호출해야 변경된 관계가 실제 DB에 반영됩니다. teacher.students.add(student)만 했을 때, student.teacher가 자동으로 연동되는지 여부를 확인 후 로직을 구성해야 함.IsarLink<T>: 1:1 같은 단일 객체 참조 IsarLinks<T>: 1:N, M:N 같은 복수 객체 참조 .save() 를 호출해야 최종 반영됩니다. 以上이 Isar의 Links(IsarLink / IsarLinks)를 활용하여 관계를 설정하고 다루는 방법에 대한 간단하고 자세한 설명입니다. Dart 언어 초급자분들도 천천히 따라 하시면 어렵지 않게 Isar에서 관계형 구조를 표현하실 수 있을 것입니다. 궁금한 점이 있다면 추가로 질문해 주세요!
아래 내용은 첨부된 이미지를 토대로 Isar에서 제공하는 IsarLinks에 대한 설명을 최대한 빠짐없이 정리한 것입니다.
Dart 언어 초급자 분들도 이해하기 쉽도록 각 용어를 풀어서 설명하고, 필요한 부분에 코드 예시와 상세한 주석을 포함했습니다.
“It would make more sense if the student from the previous example could have multiple teachers. Particularly, Isar has
IsarLinks, which can contain multiple related objects and expose a many relationship.”
즉, 이전 예시에서 한 학생이 하나의 선생님만 가질 수 있도록 IsarLink<T>를 사용했다면, 실제로는 한 학생이 여러 선생님에게 배울 수 있으므로 다수의 관련 객체를 연결해줘야 합니다.
이때 사용하는 것이 IsarLinks<T>이며, 이를 통해 1:N, M:N 등 여러 객체와의 관계를 설정할 수 있습니다.
“
IsarLinks<T>extendsIsarLink<T>and exposes all the methods that are allowed on a single link. This includes store links,save()etc. To persist the changes, calllinks.save().”
IsarLinks<T>는 IsarLink<T>를 상속(extends) 하여, 원래 IsarLink가 가지고 있던 메서드들을 그대로 사용할 수 있게 해줍니다. add(), remove(), save(), load(), … add()로 추가하거나, remove()로 삭제할 수 있습니다. save() 메서드를 호출해야 DB에 실제로 반영됩니다.“Internally both
IsarLinkandIsarLinksare represented in the same way. You can upgrade the previousteacherfrom before to anIsarLinks<Teacher>to assign multiple teachers to a single student without losing data.”
즉, 이전 코드에서 학생의 teacher(단수 링크)를 teachers(복수 링크)로 바꿔줄 때,
Isar가 내부적으로 링크 정보를 같은 필드(같은 이름)로 인식하여, 기존 데이터 손실 없이 “단일 링크”를 “다중 링크”로 업그레이드할 수 있습니다.
“This works because we did not change the name of the link (
teacher), so Isar remembers it from before.”
- 만약
teacher→teachers로 아예 이름을 바꾼다면, 새로운 필드로 인식하여 추가 마이그레이션 처리가 필요할 수 있습니다.- 이름을 그대로 두면, Isar가 기존 링크 정보를 이어받아 아무런 문제 없이 다중 링크로 전환합니다.
아래 예시는 한 학생이 여러 선생님을 가질 수 있도록 IsarLinks<Teacher>를 사용한 예시입니다.
(이미지에서는 teacher 필드를 유지하면서 내부만 바꿨다고 나와 있지만, 여기서는 혼동을 막기 위해 teachers라는 이름으로 예시를 들었습니다.)
import 'package:isar/isar.dart';
// 선생님 모델
class Teacher {
Id id = Isar.autoIncrement; // 고유 식별자
late String name;
}
// 학생 모델
class Student {
Id id = Isar.autoIncrement; // 고유 식별자
late String name;
// 여러 명의 선생님과의 관계를 IsarLinks를 통해 표현
final teachers = IsarLinks<Teacher>();
}
void exampleMultipleTeachers(Isar isar) async {
// 새로운 학생 객체 생성
final newStudent = Student()
..name = '김학생';
// 새로운 선생님들 객체 생성
final teacherOne = Teacher()
..name = '홍길동';
final teacherTwo = Teacher()
..name = '이수진';
// 트랜잭션을 통해 DB에 객체를 저장하고 관계를 설정
await isar.writeTxn(() async {
// (1) 학생, 선생님 객체를 우선 put
await isar.students.put(newStudent);
await isar.teachers.put(teacherOne);
await isar.teachers.put(teacherTwo);
// (2) 학생이 여러 선생님을 가질 수 있도록 링크 설정
// add()로 여러 객체 추가
newStudent.teachers.add(teacherOne);
newStudent.teachers.add(teacherTwo);
// (3) save()를 호출해 DB에 반영
await newStudent.teachers.save();
});
// 이후에 student.teachers를 통해
// 학생이 연결된 선생님들을 쉽게 조회 가능
}
모델 정의
@collection 데코레이터를 사용해 Isar에 테이블(컬렉션)로 인식하도록 합니다. Id)가 필요하며, 여기서는 Isar.autoIncrement를 사용해 자동 증가 ID를 부여합니다.Student 모델에서 IsarLinks<Teacher>를 선언하여 여러 Teacher들을 담을 수 있도록 합니다.데이터 생성 및 트랜잭션
isar.writeTxn() 안에서만 실제 데이터베이스에 쓰기 작업을 할 수 있습니다. (트랜잭션) isar.students.put(...), isar.teachers.put(...)를 통해 모델별 컬렉션에 객체를 저장합니다.링크 설정
newStudent.teachers.add(teacherOne): 학생-선생님 관계를 add() 메서드로 설정합니다. 저장(save())
save() 메서드를 호출해야 변경 사항이 DB에 반영됩니다. IsarLinks<T>는 단일 객체를 연결하는 IsarLink<T>와 달리, 다수의 객체와의 관계를 직관적으로 처리할 수 있습니다. @Backlink나 IsarLink 필드가 있었다면, 필드 이름을 유지하거나 적절히 마이그레이션해서 데이터가 유지되도록 해야 합니다. save()를 호출해야 실제 DB에 반영됩니다. 위 내용처럼, 기존에 단일 링크(IsarLink<Teacher>)였던 부분을 IsarLinks<Teacher>로 바꾸면 한 학생이 여러 명의 선생님을 가질 수 있는 구조를 구현할 수 있습니다. 이름을 변경하지 않으면 기존 데이터가 그대로 유지된다는 점도 기억해 두시기 바랍니다.
아래 내용은 첨부된 이미지를 토대로 Isar에서 제공하는 Backlinks와 Links 초기화 방법 등에 대해 최대한 빠짐없이 정리한 것입니다.
Dart 언어 초급자 분들도 이해하기 쉽게, 어려운 용어를 풀어서 설명하고, 코드 예시와 상세한 주석을 포함했습니다.
“I hear you ask, ‘What if we want to express reverse relationships?’ Don’t worry! We’ll now introduce backlinks.”
일반적으로 링크(Link)는 한 객체에서 다른 객체로 향하는 단방향 관계를 정의합니다.
하지만 때로는 역방향(Reverse) 관계가 필요할 수 있습니다.
예를 들어,
Backlinks는 바로 이러한 역방향 관계를 쉽게 표현할 수 있도록 도와주는 기능입니다.
“Backlinks are links in the reverse direction. Each link always has an implicit backlink. You can create it explicitly for easier relationship management. We can store any additional / memory resources on them as well, rename and rename them without issue.”
import 'package:isar/isar.dart';
class Teacher {
Id id = Isar.autoIncrement; // 고유 식별자
late String name;
// 백링크 정의
// 어떤 Student가 이 Teacher를 가리키는지 '역방향'으로 조회 가능해짐
// to: 'teacher' -> Student 모델에 있는 teacher 필드(또는 Link)의 이름
(to: 'teacher')
final students = IsarLinks<Student>();
}
class Student {
Id id = Isar.autoIncrement; // 고유 식별자
late String name;
// 학생 입장에서는 단일 Teacher만 가리킨다고 가정 -> IsarLink
final teacher = IsarLink<Teacher>();
}
설명
1.Teacher모델에@Backlink(to: 'teacher')로 역방향 링크를 선언하면, Isar가Student.teacher를 거꾸로 탐색해 “이 Teacher를 가리키는 Student가 누구인지”를 쉽게 찾을 수 있게 합니다.
2.students필드는 읽기 전용(읽기 시 편리함)으로 사용하는 것이 일반적이며, 실제로는Student.teacher를 설정할 때 관계가 결정됩니다.
3.students에 직접 무언가를 넣거나add()하는 대신, 학생 쪽 Teacher 링크(student.teacher.value = someTeacher)를 업데이트하면 해당 Teacher의studentsBacklink가 자동으로 갱신됩니다.
“We need to specify the link to which the backlink points. It is possible to have multiple different links between two objects.”
Teacher 모델이 주임교사로서의 관계, 담임교사로서의 관계 등 여러 링크가 Student 모델에 따로 있으면, 각각에 대해 @Backlink(to: '...' )를 따로 선언할 수 있습니다.“
IsarLinkandIsarLinkshave a zero-arg constructor, which should be used to assign the link property when the object is created. And you cannot move the link to another object after it is initialized.”
Isar에서 IsarLink나 IsarLinks 변수를 선언할 때, 다음과 같이 기본 생성자를 사용합니다:
class MyModel {
Id id = Isar.autoIncrement;
// Link 혹은 Links는 기본 생성자를 사용 (파라미터 없는 생성자)
final link = IsarLink<OtherModel>();
final links = IsarLinks<AnotherModel>();
// 일반적인 필드들
late String name;
}
IsarLink<T>() / IsarLinks<T>()로 선언만 하면 됩니다. student.teacher.value = someTeacher; 같은 식으로 관계를 수정해 주어야 합니다.아래 예시는 Teacher가 여러 학생을 가질 수 있고(Student.teacher), 그 역방향으로 Teacher.students Backlink를 통해 학생 목록을 조회하는 전체 과정을 보여줍니다.
import 'package:isar/isar.dart';
// Teacher 모델
class Teacher {
Id id = Isar.autoIncrement;
late String name;
// Backlink로 Student 모델에서 'teacher' 필드를 역참조
(to: 'teacher')
final students = IsarLinks<Student>();
}
// Student 모델
class Student {
Id id = Isar.autoIncrement;
late String name;
// 한 명의 Teacher를 가리키는 링크
final teacher = IsarLink<Teacher>();
}
void createDataWithBacklinks(Isar isar) async {
// Teacher 생성
final teacherHong = Teacher()
..name = '홍길동';
// Student 생성 (두 명)
final studentKim = Student()
..name = '김학생';
final studentLee = Student()
..name = '이수강';
// 트랜잭션 시작
await isar.writeTxn(() async {
// 1) Teacher, Students를 각각 DB에 저장
await isar.teachers.put(teacherHong);
await isar.students.put(studentKim);
await isar.students.put(studentLee);
// 2) 학생 쪽에서 Teacher를 가리키도록 설정
// 이 과정을 통해 Teacher 홍길동을 역참조하는 Backlink가 자동으로 생김
studentKim.teacher.value = teacherHong;
studentLee.teacher.value = teacherHong;
// 3) Link(teacher) 쪽 수정 후 반드시 save() 호출
await studentKim.teacher.save();
await studentLee.teacher.save();
});
// 이제 Teacher의 students Backlink로 어떤 학생들이 홍길동을 가리키는지 확인 가능
// Backlink는 readOnly로, DB에서 자동으로 가져옴
// 필요하다면 load()나 loadSync()를 사용하여 DB로부터 즉시 읽을 수 있음
await isar.writeTxn(() async {
// Backlink 데이터를 가져오기 위해 load()를 호출(비동기)
await teacherHong.students.load();
// teacherHong.students에 연결된 모든 Student 정보를 볼 수 있음
for (final s in teacherHong.students) {
print('${s.name} 학생이 ${teacherHong.name} 선생님을 가리키고 있습니다.');
}
});
}
@Backlink(to: 'teacher')
Teacher 모델에 선언된 students는 Student 모델의 teacher 필드를 역참조합니다. Student.teacher가 홍길동을 가리키면, Teacher.students Backlink 목록에 해당 Student가 자동으로 추가됩니다.Link 저장
studentKim.teacher.value = teacherHong;로 관계를 설정한 뒤에는 studentKim.teacher.save()를 호출해야 합니다. teacherHong.students)은 자동으로 업데이트되므로, 거기에는 별도로 save()를 호출할 필요가 없습니다(읽기 전용 개념).Backlink 조회
teacherHong.students.load()를 통해 DB에서 실제로 데이터를 가져올 수 있습니다. teacherHong.students를 순회하면, 현재 이 Teacher를 가리키고 있는 Student 객체들을 확인할 수 있습니다.@Backlink(to: '링크필드명') 데코레이터를 사용해 역방향 관계를 선언합니다. IsarLink<T>, IsarLinks<T>)를 초기화할 때는 기본 생성자를 쓰며, “링크를 다른 객체로 옮기는 것”은 허용되지 않습니다. 이상으로 Isar의 Backlinks 및 Links 초기화 방법에 대해 알아보았습니다.
초급 개발자 분들도 천천히 예제를 따라 해보시면 어렵지 않게 역방향 관계를 구축할 수 있을 것입니다.
추가 궁금증이 생기면 언제든 질문 주세요!