Skip to main content

관계란

데이터베이스의 테이블은 서로 관련되어 있는 경우가 많습니다. 예를 들어 블로그 게시물에는 다수의 댓글이 있거나, 주문은 그것을 수행한 사용자와 관련되어 있습니다. Eloquent는 이러한 테이블 간의 관계를 간단하게 정의·조작할 수 있는 구조를 제공합니다. 관계는 Eloquent 모델의 메서드로 정의합니다.
이 페이지에서는 User, Post, Comment, Tag 같은 구체적인 모델을 예로 사용합니다. 이 모델들과 테이블은 미리 생성되어 있다는 전제로 진행합니다.
Eloquent가 대응하는 주요 관계는 다음과 같습니다.

hasOne(1대1)

hasOne은 한 모델이 다른 모델을 하나만 가지는 관계를 나타냅니다. 예를 들어 User가 하나의 Profile을 가지는 경우입니다.

관계 정의

User 모델에 profile 메서드를 정의합니다.
Eloquent는 부모 모델 이름을 기반으로 외래 키를 자동으로 추측합니다. 이 경우 profiles 테이블에 user_id 컬럼이 있다고 가정합니다.

관련 데이터 가져오기

정의된 관계는 프로퍼티로 접근할 수 있습니다.
관계 메서드를 프로퍼티로 호출하면 Eloquent가 자동으로 쿼리를 발행해 관련 데이터를 반환합니다. 이를 “다이내믹 관계 프로퍼티”라고 합니다.

belongsTo(1대1의 역방향)

belongsTohasOne의 역방향으로, 외래 키를 가진 쪽의 모델에 정의합니다. ProfileUser에 속하는 관계를 나타냅니다.

관계 정의

Eloquent는 관계 메서드명에 _id를 붙인 컬럼명(이 경우 user_id)을 외래 키로 사용합니다.

관련 데이터 가져오기

hasMany(1대다)

hasMany는 하나의 부모 모델이 여러 자식 모델을 가지는 가장 자주 사용되는 관계입니다. 예를 들어 하나의 Post가 여러 Comment를 가지는 경우입니다.

관계 정의

Eloquent는 comments 테이블에 post_id 컬럼이 있다고 가정합니다.

관련 데이터 가져오기

hasMany의 관계는 컬렉션을 반환합니다.
쿼리에 조건을 추가할 수도 있습니다.

역방향 관계(belongsTo)

댓글에서 게시물을 참조하려면 Comment 모델에 belongsTo를 정의합니다.

belongsToMany(다대다)

다대다 관계는 양쪽 모델이 서로 여러 관련을 가지는 경우에 사용합니다. 예를 들어 Post가 여러 Tag를 가지고, Tag도 여러 Post에 속하는 경우입니다.

테이블 구조

다대다에는 중간 테이블이 필요합니다. PostTag의 경우 post_tag라는 중간 테이블을 준비합니다.
중간 테이블명은 Eloquent가 관련된 두 모델명을 알파벳 순으로 결합해 자동으로 추측합니다(post + tagpost_tag).

관계 정의

Tag 쪽에도 동일하게 정의함으로써 역방향에서도 참조할 수 있습니다.

관련 데이터 가져오기

관련 데이터 추가·삭제

attach로 중간 테이블에 레코드를 추가하고, detach로 삭제합니다.
sync를 사용하면 지정한 ID 이외의 중간 테이블 레코드를 자동으로 삭제하고, 전달한 ID만 관련으로 남도록 갱신합니다.

Eager 로딩

N+1 문제란

관계를 프로퍼티로 접근하면 Eloquent는 그때마다 쿼리를 발행합니다. 이를 “지연 로드(Lazy Loading)“라고 합니다. 루프 안에서 관계에 접근하면 N+1 문제가 발생합니다.
게시물이 100건이면 총 101회의 쿼리가 발행됩니다. 이는 성능에 큰 영향을 줍니다.

with()에 의한 Eager 로딩

with() 메서드를 사용하면 관련 데이터를 한 번에 가져올 수 있습니다. 이를 통해 쿼리는 2회만으로 억제됩니다.
실행되는 SQL은 다음 두 가지뿐입니다.

여러 관계를 동시에 Eager 로드

배열로 여러 관계를 지정할 수 있습니다.

중첩된 Eager 로드

점 표기법으로 중첩된 관계도 Eager 로드할 수 있습니다.
Eager 로딩은 N+1 문제를 방지하기 위해 매우 중요합니다. 루프 안에서 관계에 접근할 때는 항상 with()를 사용하는 습관을 들입시다.

다음 단계

Eloquent 입문

Eloquent의 기본 CRUD 조작으로 돌아가 복습합니다.
마지막 수정일 2026년 7월 13일