본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 18. 18:45

AI는 집약(Aggregate)을 인식하지 못한다. Domain Driven Design으로 AI에게 올바르게 지시하기

요약

AI가 복잡한 도메인 모델의 관계를 스스로 파악하지 못하는 한계를 지적하며, Domain Driven Design(DDD)의 집약(Aggregate) 개념을 활용해 AI에게 더 정확하게 지시하는 방법을 제안합니다.

핵심 포인트

  • AI는 단순 기능 구현에는 강하지만 객체 간의 복잡한 관계는 인식하지 못함
  • 도메인 모델의 집약(Aggregate) 개념을 이해하고 프롬프트에 반영해야 함
  • 데이터 간의 제약 조건과 연관성을 명시하여 AI의 환각과 오류를 방지

현재, AI를 이용한 개발 연습으로서 Google AI Studio로 「영화 예약 사이트 (스태프 측)」를 만들고 있습니다.

우선 다음과 같이 Google AI Studio에 지시를 해보았습니다.

현재, 영화 예약 시스템을 개발 중입니다. localhost:8080/api/staff/login 에 접속하여 로그인하는 화면을 만들어 주세요.
인증에는 JWT 토큰을 사용하고, 로그인 성공 후에는 대시보드로 전환되도록 구현해 주세요.

완성된 화면이 이렇습니다. 로그인 화면과 빈 대시보드만 있으면 된다고 생각했는데, 더미 영화 스케줄까지 있다니! 대단해! 하지만 다른 회사의 이름이 들어가 있는 건 안 된다고 생각합니다!

대시보드

대시보드 아래에는 지시하지 않았는데 좌석을 예약하는 기능이 있습니다!

좌석을 클릭하면 관리 측에서 특별히 좌석을 잡을 수 있습니다! 좌석 색이 바뀌고, WebAPI에 리퀘스트까지 보내고 있어요, 대단해!

이것이 「영화 예약 사이트」라고만 입력한 AI가 생성한 예약 리퀘스트입니다!!

PUT (Local simulation) /api/seats/toggle/F-8

…아니, 확실히 좌석에 액세스는 하고 있습니다. 하고는 있지만… 언제의 어느 상영관 좌석인 건데!?

toggle는 화면·이미지 조작을 의미하므로 이름도 좀 다르네요. booking 정도는 생성해 주길 바랐습니다…

관객이 좌석을 잡을 수 없다면 영화를 상영하는 쪽은 어떨까요?

AI에게 「상영 예정을 관리하는 화면을 만들어줘!」라고 하니 상영관에 대해 시간을 지정하여 「영화명을 입력하는」 화면을 만들어 주었습니다.

아까워! TODO 리스트나 회의실·회장 예약 시스템 같은 것이라면 그래도 괜찮을 것 같습니다만!

AI에 의한 코드 생성 기사는 많이 있습니다. TODO 리스트 같은 것은 「TODO 리스트를 만들어줘」라고만 해도 만들어 준다고 하더군요!

그렇다면 왜 영화 예약은 할 수 없는 걸까요? AI는 TODO 리스트처럼 대상만을 조작하거나 화면에 정보가 모여 있는 코드에는 강하지만, 쓰여 있지 않은 것은 모르기 때문입니다.

영화 예약이라고 해도 실제로는 좌석을 예약하고 있다는 점까지는 AI가 인식하고 있습니다. 실제로 코드나 화면이 있는 것이겠지요.

하지만 좌석과 영화가 어떻게 관련되어 있는지는 모르고, 좌석이 상영관에 있다는 것도, 영화가 언제의 어느 상영관에서 상영되는지도 모릅니다.

하나의 상영관에서 동시에 여러 편의 영화를 상영할 수 없다는 것도 모를지 모릅니다. 스태프 화면에 「상영 스케줄 기능」 같은 것을 만들면 상영 예정이 겹칠 수도 있겠네요!

「집약이란 무엇인가」로 구글링하거나 AI에게 물어보면 이렇게 대답합니다.

「집약 (Aggregate) 이란, 관련된 오브젝트 군을 하나의 유닛으로서 관리하기 위한 수법입니다」

이것만으로는 무엇을 말하고 싶은지 알 수 없겠지요?

「집약」이란 「서로 영향을 주거나 어떠한 제한이 있으면서 존재하는 오브젝트의 묶음」입니다.

예를 들어, 지금의 영화관 (시네마 컴플렉스) 에는 상영관이 여러 개 있지만, 상영관에는 각각 별도의 이름이 붙어 있습니다. 서로 다른 영화를 상영하는데 방 이름이 같다면 어디서 상영하는지 알 수 없겠지요!

또한, 영화를 상영할 때는 하나의 상영관에서 하나의 스크린에 영화를 상영합니다. 마음만 먹으면 여러 개의 스크린을 설치할 수도 있겠지만, 여러 편의 영화를 같은 방에서 동시에 상영해도 분명 즐겁지는 않을 것입니다.

또한, 구성하는 오브젝트에 따라 전체에 변화가 생기기도 합니다. 예를 들어 특별한 음향 기재를 도입하여 폭음으로 상영할 수도 있고 (그때는 방의 방음을 강화하지 않으면 소리가 새어 나가겠지요!), 기재가 고장 나면 영화 상영 자체를 할 수 없게 될 것입니다.

특히 잊기 쉽거나… 혹은 아무도 신경 쓰지 않는 것일지도 모르지만, 집약은 「사람은 그 일부에만 주목하고 있지만 실제로는 집약 자체나 집약 내의 다른 오브젝트 이야기를 하고 있는」 경우가 많다는 점에 주의해야 합니다.

영화 보러 가는 것을 「영화관에 간다」라고 할 수는 있지만, 「어제 ○○ 영화를 보러 갔다」라고는 할 수 있어도 「어제 영화관에 갔다」라고 하면 그것으로 무엇을 보고 왔냐는 생각이 들게 되지요.

또한, 만약 소크라테스가 대머리였다고 가정하고 「소크라테스의 머리는 대머리이다」라고 할 때 「머리」라는 단어를 넣어도 정보량은 늘어나지 않습니다. 사람의 경우라면 말이죠. AI는 「머리」를 지시하지 않으면 알 수 없는 것이 있으므로 뜻밖의 곳에서 지시 누락이 발생합니다. (오브젝트의 상태를 나타내기 위해 소크라테스를 사용하는 것은 아리스토텔레스로부터의 전통이지 악의가 있는 것은 아닙니다!)

Domain Driven Design (DDD)라는 모델링 사고방식이 있습니다.

"DDD란?"이라고 구글링하거나 AI에게 물어보면 다음과 같이 답변합니다.

"소프트웨어 개발에 있어 '실제 업무나 비즈니스 규칙 (Domain)'을 시스템의 중심에 두는 설계 기법"

이것도 도대체 무엇을 말하려는 건지 모르겠군요! 애초에 DDD가 아니더라도 시스템을 개발할 때는 실제 업무나 비즈니스 규칙을 시스템의 중심에 두는 것이 당연하지 않습니까?

애초에 Domain = 업무라는 것은 오역입니다. Domain이란 Core가 주체성을 갖는 영역이라는 의미입니다.

Core는... 업무의 중심이 아닙니다. 비즈니스라면 "실제로 돈을 지불하는 사람", 어떤 서비스라면 "그 서비스를 통해 실현하고자 하는 것 또는 서비스를 받는 사람"이 Core입니다.

예를 들어, 영화관의 Core는 영화를 상영하는 것이 아닙니다.

관객에게 요금을 받는 것, 관객이 영화를 보게 하는 것입니다.

관객 없이 영화를 상영할 수도 있겠지만, 그것은 비즈니스라고 할 수 없겠지요?

하지만 한마디로 영화를 보게 한다고 해도, 이를 위해 해야 하는 일, 즉 업무가 많이 존재합니다. 이 업무를 도메인(Domain)이나 애그리거트(Aggregate)로 분할하여 설계해 나가는 것이 Domain Driven Design입니다.

그럼 모델링...을 하기 전에, 도메인을 분할합니다.

도메인의 차이·도메인의 경계란 무엇일까요? 그것은 "결정권을 가진 자가 누구인가"입니다.

도메인은 흔히 코어 서브도메인(Core Subdomain)·서포트 서브도메인(Support Subdomain)·제너릭 서브도메인(Generic Subdomain)의 세 가지가 있다고 말합니다.

어떤 기능이 어느 도메인에 속하는지 판단하기 어려울 때가 있지만, 결정권자의 차이로 명확하게 나눌 수 있습니다.

저는 API도 결정권자의 차이로 나누는 것을 선호합니다.

영화의 코어 도메인은 영화관 입장에서 보면 영화를 보여주고 요금을 받는 것(덤으로 굿즈나 푸드도 사게 하는 것)입니다. 하지만 영화를 강제로 보여주고 요금을 받게 할 수는 없습니다.

어떤 영화를 언제 볼지는 관객이 결정하므로, 코어 도메인의 중심에는 관객이 있습니다.

영화를 보려면 예약과... 상영 예정 정보를 알 필요가 있겠지요.

이것들을 비즈니스 로직(Business Logic)으로서 이번에는 백엔드(Backend)에 구현하고, WebAPI로 접근할 수 있도록 하겠습니다.

API의 루트(Route)는... 관객이라고 해도 예약을 할 수 있는 회원과 상영 예정을 볼 수 있는(회원을 포함하여) 일반인이 있으니 /api/members/와 /api/public/ 두 개를 두는 것이 좋을까요? 약간 오버엔지니어링(Over-engineering)인 느낌도 드네요.

영화 상영 예정을 관리하거나,

관객 대신 예약을 넣는 일은... 아마 별로 없을 것 같지만,

할인용 신분증을 잊었을 때 일반 티켓으로 전환하는 업무 같은 것은 있을지도 모르겠네요.

새로운 영화관 등록이나 방 추가 같은 것은... 가끔만 할 것 같은 느낌이 들지만, 장비 고장이나 오염으로 방이나 좌석을 사용 금지하는 정도는 통상적으로 발생할 수 있습니다.

API의 루트는 /api/staffs로 합니다.

영화를 보고·보여주기 위해서는 상영해야 합니다. 여기서 물리적 제약이 발생합니다.

  • (상영과는 상관없지만) 기업은 여러 개의 영화관을 가지고, 영화관에는 여러 개의 방이 있으며, 방에는 여러 개의 의자가 있다
  • 방에 대해 특정 시간대에 상영한다
  • 하나의 방에서 동시에 상영할 수 있는 영화는 하나뿐이다
  • 상영하는 영화는 구매한 영화 리스트에서 선택한다
  • 좌석에는 방마다 고유한 좌석 번호가 붙어 있다
  • 필름 영화는 여러 방에서 동시에 상영할 수 없다 (지금은 이런 제약이 없죠, 몇 년 전 이야기일까요?)

따라서 백엔드는 DB 상에 있는 방과 영화를 시간을 지정하여 연결하게 됩니다.

상영 계획 전에 방과 영화의 CRUD를 만들어 둡시다. 사용하지 않을 것 같기도 하지만, AI에게 위의 정보와 "방의 CRUD와 영화의 CRUD를 /api/staffs에 만들어줘"라고 말하기만 해도 만들어 줍니다.

여기서부터는 로컬의 IntelliJ IDEA에서 Spring Boot 프로젝트를 만들고 Gemini plugin을 통해 만들게 합니다. 완성된 WebAPI를 Google AI Studio에 전달하면 제대로 호출해 줄 것입니다.

먼저 물리적인 방의 애그리거트(Aggregate)를 만듭니다. 물리적인 제한(예를 들어 기본적으로 방이 추가되는 일은 없다)이나 명칭적인 제한(Unique 제약)이 많으므로 등록 시에 체크가 필요합니다.

영화관은 Theater가 일반적이지만, 상영실은 TOHO에서는 Screen, 시네마시티(Cinema City)에서는 Studio라고 할 수 있겠네요. 참고로 AI에게 쓰게 하면 상영실이 Theater가 되는데, 이는 건물 자체를 나타내는 단어입니다...

상영은 Screening이므로 Screen이 영화관 느낌이 날까요? Studio라고 하면 극장 느낌이 나려나?

AI에게 다음과 같이 지시하는 것만으로 CRUD를 만들어 주었습니다. Controller는 theaters밖에 만들어 주지 않아서 지시를 반복해야 하지만, 전부 지정하면 전부 만들어 주는 걸까요...?

영화 예약 시스템을 만듭니다. 다음 Entity의 집약(Aggregate)을 만들어 domain/entity에 저장해 주세요.
영화 예약 대상인 영화관 그룹은 여러 개의 영화관(theater)으로 구성되어 있습니다. 영화관은 여러 개의 방(screen)으로 구성되어 있습니다. 방에는 여러 개의 좌석이 있습니다.
영화는 작품별로 관리하며, 각각의 방에서 특정 시간대에 상영합니다. 이는 다대다(Many-to-Many) 관계가 있다는 뜻입니다.
...

AI에 대한 지시는 위에 정리되어 있지만, 설계로 보자면...

영화는 아마 배급사가 타이틀이나 상영 시간 등의 정보를 가지고 있겠죠. 분명 그 정보에 영화관 측의 정보(장르 태그 등?)를 추가하고 있을 것입니다.

지금은 타이틀과 상영 시간만 있어도 괜찮겠네요.

상영 예정(Screening Schedule)은 방(screen)에 대해 여러 개 존재합니다. 이것은 시간을 조정한다는 조건이 있으므로, 단순한 연관 관계가 아니라 집약(Aggregate)으로 만들어 제약 조건(Constraint)을 구현해야 합니다.

상영은 영화를 하나만 상영합니다. 구매한 영화만 상영할 수 있지만, 그것은 '구매한 영화 집약'의 문제이며, 스크린에 영화를 투사하는 것은 집약의 일부이긴 하지만 특별한 제약은 없습니다. 지금은요.

예전에 필름으로 영화를 상영하던 시절에는 동일한 필름을 여러 방에서 동시에 사용할 수 없다는 물리적인 제한이 있었으므로, 그 시대에는 집약의 제약 조건으로 구현해야 했을 것입니다. 집약의 관계성도 시대에 따라 변하는군요!

또한, 'STOP! 영화 도둑'이나 예고편 등의 상영을 거친 뒤 본편이 시작됩니다. 메인과는 별개의 콘텐츠이므로 additional(추가 사항)로 할까요?

청소나 관객 교체 등의 시간도 있지만... 관객과는 관계가 없으려나? 영화 상영 시간은 일정하지 않으므로 이러한 인터벌(Interval)은 일정하지 않지만, 그렇다고 0인 것도 아닙니다. 지금은 additional로 묶어 두되, 최소한의 intervalTime 등을 설정할 수 있도록 해야 할지도 모르겠습니다.

여담이지만, AI에게 연장 시간(extension time)이 있다고 지시하면 additional이 되지 않습니다. extend 같은 것이 되어 버렸습니다. 추가적인 별도 영상의 상영 시간이 있다고 지시해야 합니다.

WebAPI는 기본적으로 집약(Aggregate)에 대해 액세스합니다.

상영 예정은 방에 강하게 의존하므로 다음과 같이 될 것입니다. theater까지 모델링한 것은 오버엔지니어링(Over-engineering)이었을까요...?

POST /api/staffs/theaters/{id}/screens/{id}/screening
{"movieId":1, "startTime": "12:00", "additionalTime":5}

여기까지 해서 영화를 예약할 때의 서포트 서브도메인(Support Subdomain) 측 CRUD가 완성되었습니다!

영화를 예약할 때, 어떤 영화의 어떤 상영의 어떤 좌석인지를 지정하는 것은 사용자이므로 /api/members/에 만듭시다.

예약에도 여러 가지가 있지만, 원래는 좌석과 시간의 예약을 기입하는 것이었으므로 Booking이겠네요.

가족 몫의 좌석도 동시에 예약하므로 좌석은 여러 개를 지정할 수 있는 것으로 하겠습니다.

영화에 가족 할인이 있으려나...? 뭐, 아이는 보통 어린이 요금이므로 관객(audience)도 필요하겠네요. 이것은 아마 상수(Constant)일 것입니다.

실패 사례: 이것을 아래와 같은 API로 만들면 부자연스러워집니다.

POST /api/members/theaters/{id}/screens/{id}/screenings/{id}
{"seats":[{"seatName":"A-01","audience":"MEMBER"}]}

관객은 볼 영화를 결정하기 위해 영화 상영 예정 목록을 볼 것입니다. 상영 예정 목록에서 방을 의식하는 것은 이상하죠!

가장 단순한 예약 API는 분명 이렇게 될 것입니다.

POST /api/members/screenings/{id}
{"seats":[{"seatName":"A-01","audience":"MEMBER"}]}

상영 목록이 필요할 때... 이것은 누구나 볼 수 있으므로 public 사용자용이겠네요.

활동 범위 내에 여러 영화관이 있는 사람도 있으므로 영화관을 지정하거나 지정하지 않거나 할 것 같습니다.

// 전체 상영 예정
GET /api/public/screenings/
// 특정 영화관의 상영 예정
...

이 정보들을 모두 AI에게 입력하여 코드를 생성하게 합니다! (mermaid 코드는 글을 쓸 때 작성했기 때문에 일본어로 입력했습니다)

Controller는 평범하게 만들어 주었고 Service는...

좌석 존재 여부 체크나 중복 체크도 해주고 있네요! Audience가 빠져 있을 때는 0원이 되거나 요청(Request)·응답(Response)을 그대로 입출력하고 있습니다만, 다음에 할 때는 미리 .md 파일에 전제로 작성해 두면 되겠네요!

백엔드(Backend) 측은 완성입니다!

package com.example.geminiassistdemo.domain.service
@Service
class BookingService(
...

그럼 좌석 예약 화면도 생성시켜 보겠습니다. 오랜만에 사용하는 Google AI Studio입니다.

회원 측 화면은 아직 만들지 않았기 때문에, 스태프가 회원을 대신하여 예약하는 형식(그런 업무는 없겠지만)으로... 다음과 같이 AI에게 지시를 내렸습니다.

영화 예약 화면을 만들어 주세요. 영화 예약은 영화의 상영 예정(Screening)에 대해 좌석(seat)을 지정하는 방식으로 진행합니다. API는 POST /api/staffs/members/{id}/screenings/{id}
{"seats":[{"seatName":"좌석 번호","audience":"관객 종류"}]}
상영 예정에는 영화(Movie)가 연결되어 있으며, Movie에는 이름과 상영 시간이 있으므로 예약 시에 확인할 수 있도록 해주세요.
...

Google AI Studio는 무사히 화면을 수정해 주었습니다!

이름과 Email 칸이 있는 것은 회원 확인용일까요?

전송하고 있는 요청(Request)은...

회원 ID 지시가 미흡해서 로그인 중인 스태프의 ID가 들어가 있거나 상영 ID가 이상하기도 하지만, 대략적으로는 성공인가요?

제대로 회원 측 화면으로서 만들고, 상영 예정에 대한 예약 화면으로 만들게 하면 자연스러워질 것 같습니다.

관객 종류나 요금은 아마 어딘가의 영화관 요금을 참고하고 있는 것이겠죠. 그것도 API를 설계하여 통신하도록 만들어야겠네요.

그럴듯한 프론트엔드(Frontend)를 간단히 만들 수 있는 것은 좋지만, 빠짐없이 지시를 내리는 것은 힘드네요...

이번에는 대화하면서 만들었습니다만, Claude 등으로 만들 때는 이것들을 정리해 두었다가 전달하게 되는 걸까요. 이 글을 다시 정리해서 Claude에게 먹여볼까 합니다.

마지막으로, 저는 영화관에 가본 적이 없으므로 틀린 부분을 알려주시면 수정하겠습니다!

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0