처음 적용한 DDD 설계부터 솔직한 개인 회고까지 — 한 달간 LXP(Learning Experience Platform)를 기획·설계·프로토타이핑하며 마주한 의사결정과 반성을 정리합니다.
TL;DR
- 무엇을 — '미션 기반 학습 경험'을 핵심 가치로 둔 구독형 LXP. 무료 1개월 체험 + 월 구독(₩19,000) 모델.
- 어떻게 — 이벤트 스토밍 → 유비쿼터스 언어 → CRC → Bounded Context 맵까지 DDD 정석 파이프라인을 팀 전원이 완주.
- 가장 큰 배움(설계) — 서비스와 애그리거트의 책임 분리 기준을 잡는 것이 설계 전체의 척추였다.
- 가장 큰 배움(팀) — 진짜 리스크는 기술이 아니라 인원 변수와 의사결정 속도였다.
- 가장 솔직한 반성 — 개인 프로젝트와 병행하며 집중이 분산됐고, AI가 짜준 코드를 충분히 이해하지 못한 채 달렸다.
1. 무엇을 만들었나: 구독형 LXP
LXP는 Learning Experience Platform, 즉 학습 '경험'에 초점을 둔 플랫폼입니다. 일반적인 강의 플랫폼(LMS)이 "콘텐츠를 일방향으로 전달"하는 데 그친다면, 우리가 정의한 LX는 달랐습니다.
LX(학습 경험): 학습자가 지식을 단방향으로 받기만 하지 않고, 능동적 활동과 양방향 소통 과정에서 스스로 생각하고 학습하며 겪는 경험.
제품의 타깃은 개발자·전문가였고, 포지셔닝은 "불필요한 장식을 덜어내고 오직 성장에만 집중하는 환경"으로 잡았습니다. 비즈니스 모델은 단순한 구독제로 정리했습니다.
- 무료 체험: 가입 시 1개월 구독권 자동 발급 (기본 강좌 + 커뮤니티 열람)
- 월간 구독권: ₩19,000/월 (전 강좌 무제한 + Q&A 우선 답변 + 오프라인 저장)
가장 먼저 한 일은 "이 제품의 한 줄짜리 차별점이 무엇인가" 를 팀과 합의하는 것이었습니다. 결론은 미션(Mission) 이었습니다.
2. 제품 관점: '학습 경험'을 어떻게 설계로 번역했는가
추상적인 'LX'를 구체적인 기능 가설로 바꾸는 것이 기획의 핵심이었습니다. 우리가 세운 LX 유도 메커니즘은 다음과 같습니다.
강좌마다 미션을 두고, 회원의 미션 답안을 다른 회원과 공유하여 풀이·소통 과정에서 능동적으로 학습·성장하도록 유도한다.
여기서 파생된 핵심 정책들이 곧 제품의 뼈대가 됐습니다.
- 미션을 첫 화면에 우선 노출 — "강의를 본다"가 아니라 "미션을 푼다"가 진입점이 되도록 한 의도적 설계.
- 후기 작성 자격 제한 — 강좌 후기는 해당 강좌의 미션에 1개 이상 답안을 제출한 구독자만 작성 가능. 후기의 신뢰도를 구조적으로 보장하기 위한 장치.
- 콘텐츠 접근의 게이트(Gate) — 비회원에게는 전부 비공개, 구독권이 있어야 강좌·강의·미션 열람 가능.
관점 선택: 어드민 중심 vs 사용자 중심
설계 초반, 같은 기능도 누구의 편의를 우선할 것인가에 따라 구조가 갈렸습니다. 논의 끝에 어드민(운영진) 관점을 채택했습니다.
- 채택 이유: 소수의 운영진이 관리하기 편한 구조. 강좌 공개는 어드민 승인제로 통제.
- 감수한 트레이드오프: 운영 편의를 높이는 만큼 기능 추가 요구가 늘어날 가능성이 높아진다는 점을 명시적으로 기록하고 넘어갔습니다.
이 단계에서 분명해진 건, "무엇을 만들까"보다 "누구를 위해 최적화할까"를 먼저 합의해야 이후 기능 논쟁의 기준점이 생긴다는 것이었습니다.
3. 프로세스 관점: 처음 도입한 DDD를 팀에 안착시키기
이번 프로젝트의 방법론은 DDD(도메인 주도 설계) 였고, 팀 전원이 처음이었습니다. 그럼에도 정석 파이프라인을 끝까지 밟았습니다.
이벤트 스토밍 (액터·커맨드·도메인 이벤트·정책·외부 시스템)
↓
유비쿼터스 언어 정의 (팀·코드·문서가 한 단어를 한 의미로)
↓
애그리거트 도출 + 정책·불변식 도출
↓
CRC 카드 작성 (책임·협력자 정의)
↓
Bounded Context 맵 + 애그리거트 루트 시각화 (Figma)
설계의 척추: 서비스 vs 애그리거트의 '책임 분리'
CRC 카드를 작성하며 팀이 가장 많은 시간을 쓴 지점이자, 가장 큰 합의를 이룬 지점입니다. "구독권을 생성한다", "강좌 요청을 처리한다" 같은 표현이 전부 서비스에 몰려 있었고, 이를 다음 기준으로 재정리했습니다.
| 계층 | 책임 |
|---|
| Service | 요청 유효성 확인 · 권한 확인 · 중복 여부 확인 · 관련 객체 존재 여부 확인 (= 검증/조율) |
| Aggregate / Aggregate Root | 객체 생성 · 상태 변경 · 도메인 불변식 유지 (= 실제 도메인 행위) |
이 기준 하나로 모든 도메인의 CRC가 정돈됐습니다. 예를 들어 "구독권을 생성한다"는 책임은 서비스가 아니라 구독권 애그리거트로 내려갔고, 서비스는 "적법한 회원의 요청인지 확인하고 저장을 조율"하는 역할로 좁혀졌습니다.
컨텍스트 맵: 두 개의 상류가 시스템을 떠받친다
BC 간 의존 관계를 그려보니 구조가 명확해졌습니다.
- 상류(공급자):
회원 관리, 구독권 결제 관리
- 하류(소비자):
강좌 컨텐츠, 강좌 후기, 미션 답안
- 패턴: 고객-공급자(Customer-Supplier). 거의 모든 BC가 "유효한 회원인가 / 유효한 구독권인가" 를 이 두 상류에 물어본다.
설계 시사점: 회원·구독권 BC의 검증 인터페이스가 가장 많이 호출된다. 이 두 BC의 API가 안정적이어야 나머지 BC가 흔들리지 않는다 — 즉 여기가 시스템의 단일 장애점이자 우선순위 1번이라는 결론.
돌아보면 DDD는 "설계 기법"이라기보다 팀의 언어를 통일하는 합의 도구에 가까웠습니다. 다만 그 합의 비용이 예상보다 훨씬 컸고, 이는 뒤에서 다룰 반성으로 이어집니다.
4. 기술 의사결정 관점: 트러블슈팅 기록
설계가 코드로 내려오며 마주한 의사결정들입니다. 대부분 "설계 원칙 vs 현실"의 충돌이었습니다.
① BE/FE 의존성 분리 — '프랑켄슈타인 코드'와의 결별
- 문제: FE와 BE 작업 영역이 분리되지 않은 채 개발을 시작. 미완성 BE 코드를 FE에 직접 끌어와 연동하다 보니 로직이 강하게 뒤섞인 '프랑켄슈타인 코드' 발생.
- 원인: API 명세 기반의 독립 개발 환경 부재. FE가 BE 진행 속도에 종속되며 관심사의 분리(SoC)가 무너짐.
- 결정: 절반쯤 완성된 코드를 과감히 전면 폐기. Mock 데이터/Mock API 환경을 세팅해 처음부터 구조를 다시 설계.
- 결과 & 교훈: FE가 BE에 의존하지 않는 병렬 개발 환경을 확보. 뼈아픈 재작업이었지만, 초기 API 명세 정의와 Mocking의 중요성을 실무 수준에서 체득.
한 줄 메모: "절반을 버리는 결정"은 빠를수록 싸다. 결합도가 임계점을 넘으면 유지보수 비용이 재작성 비용을 추월한다.
② 서비스 계층의 DTO 컨벤션 변경
- 기존 지침: 모든 Service 클래스는 상하위 계층과 데이터를 주고받을 때 인자·리턴 모두 DTO를 사용한다.
- 문제: 인자까지 DTO로 받으니 서비스가 HTTP 계층에 과하게 종속됨. 실제 코드가 지침과 다르게 작성되는 현상 발생.
- 변경: 인자는 원시값(primitive)으로 받고, 리턴값만 DTO로 통일.
작은 컨벤션 같지만, "서비스는 특정 전달 계층(HTTP)을 몰라야 한다"는 DDD의 책임 분리 원칙을 코드 레벨에서 관철한 결정이었습니다.
③ PaymentService → PaymentAdapter
결제는 도메인 상태를 스스로 바꾸는 애그리거트 루트가 아니라, 외부 결제 시스템과 요청/응답을 주고받는 어댑터에 가깝다고 판단했습니다. 이름이 곧 책임의 위치를 규정하기에 명칭을 PaymentAdapter로 교정했습니다. 환불 처리 역시 대행사 지연·실패 가능성을 고려해, 환불 기준 시점을 '실제 환불 완료'가 아닌 '탈퇴/취소 요청 시점' 으로 두고, 실패 건은 로그 기반으로 어드민이 후속 처리하도록 설계했습니다.
④ JWT 인증 — Bearer 누락이라는 작지만 뼈아픈 삽질
- 문제: JWT 토큰을
Authorization 헤더에 실어 보낼 때 Bearer 접두사를 빠뜨려 인증이 계속 실패.
- 교훈: 표준 스펙의 사소한 디테일 하나가 전체 인증 흐름을 막는다. 그리고 솔직히, 코드를 충분히 이해하고 있었다면 더 빨리 잡았을 종류의 버그였다. (→ 6번 회고의 'AI 의존성'과 이어진다.)
5. 팀 관점: 가장 어려웠던 건 결국 사람과 일정
기술보다 팀과 일정 관리가 훨씬 어려웠습니다. 솔직하게 기록합니다.
마주한 문제
- 인원 변수: 팀원 이탈(1명 중도 하차)과 잦은 결석. 한때 회의 결원 디폴트가 3명일 정도로 참여율이 저조했습니다.
- 업무 쏠림: 그 결과 팀 프로젝트임에도 특정 인원에게 업무와 부담이 과도하게 집중되는 구조가 됐습니다. 근본 원인은 사전에 업무 분담 기준이 명확하지 않았던 것이었습니다.
- 의사결정 지연: DDD가 낯설다 보니 도메인 이벤트·경계를 나누는 기준을 두고 토론이 무한정 길어졌고, 초반 일정이 크게 밀렸습니다. 전날 정한 사항이 다음 날 뒤집히는 일도 잦았습니다.
- 위축된 제안: '불변식 추가'를 '기능 추가'로 오해해 의견 제시를 주저하면서, 시스템 완성도를 높이는 논의가 소극적으로 흘렀습니다.
시도한 처방: 팀 문화의 명문화
문제를 인지한 뒤, 암묵적 규칙을 명시적 컨벤션으로 전환했습니다.
- 소통: Slack 스레드 활용으로 대화 추적성 확보, 무조건적인 "NO" 금지 등 긍정 언어 원칙.
- 회의: 회의 전 각자 당일 안건 숙지, '개인보다 회의 우선' 마인드, 회의록·다음 안건 작성을 돌아가며 담당, 매주 금요일 정기 회고.
- 집중: 50분 작업 / 10분 휴식 사이클, 작업별 자체 제한 시간(타임박싱) 설정.
- 온보딩: 결원 인원을 위해 변경 사항을 색상으로 강조한 온보딩 문서를 작성 → 빠른 복귀 지원.
이 중 가장 효과적이었던 건 중간 회고였습니다. 설계와 협업 프로세스가 정체됐을 때 브레이크를 걸고 방향을 재정비한 시도 자체가 팀을 다시 움직이게 만들었습니다.
그리고 솔직히, 위에서 말한 '집중력 분산'의 한 축에는 나 자신도 있었습니다. 그 이야기를 다음 장에 그대로 남깁니다.
6. 회고: 솔직하게 남기는 반성
좋았던 것
- 의견이 부딪힐 때 회피하지 않으면서도 서로 존중하려 노력했다. 충돌을 피하기보다, 부딪히되 존중하는 쪽을 택한 것은 잘한 일이다.
- DDD 설계를 처음 경험했다. 이벤트 스토밍부터 BC 시각화까지 전 과정을 끝까지 밟아본 경험 자체가 가장 큰 수확이다.
- 팀 차원에서도 새 기술 스택(Next.js) 학습, 타임박싱 도입, 정석 파이프라인 완주는 분명한 성과였다.
솔직히 부족했던 것
- 본질을 흐렸다. 이번 프로젝트 외에 개인 프로젝트를 병행했고, 거기에 집중이 쏠리면서 정작 본질이어야 할 팀 프로젝트에 온전히 몰입하지 못했다. 앞 장에서 말한 팀의 '집중력 분산'에 내가 일조한 셈이다.
- 시간 관리가 부족했다. 여러 일을 동시에 벌여놓고 우선순위를 명확히 두지 못했다.
- AI 의존도가 높았다. AI가 짜준 코드를 충분히 이해하지 못한 채 넘어간 경우가 많았다. 4번에서 적은 JWT
Bearer 누락 버그가 그 단면이다 — 코드를 제대로 이해하고 있었다면 애초에 덜 헤맸을 문제였다.
- 개인 공부도 어중간했다. 팀 프로젝트, 개인 프로젝트, 개인 공부 어느 쪽도 "제대로 했다"고 말하기 어려운 한 달이었다.
역설적인 결과
- 개인 프로젝트는 유의미한 트래픽을 받았다. 성과 자체는 났다.
- 그러나 그 대가로 팀 회고에서 내가 집중하지 못한 부분이 그대로 드러났다.
- 그렇다고 개인 공부를 제대로 했다고 보기도 어려웠다. 결국 한 곳에서만 결과가 났고, 나머지는 어정쩡하게 남았다.
다음엔 이렇게 (개선)
- 투두(To-Do)를 작성한다. 일을 벌여놓고 추상적으로 진행하는 경향이 있다. 할 일을 가시화해서 무엇을 끝냈고 무엇이 남았는지 눈으로 확인하며 진행한다.
- 코드 리뷰를 적극적으로 한다. AI가 작성한 코드를 그냥 받아들이지 않고, 이해한 뒤 넘어가는 것을 원칙으로 삼는다. 리뷰는 코드를 이해하는 가장 빠른 강제 장치다.
- 개인 욕심을 버린다. 여러 일을 동시에 잘하려는 욕심이 결국 전부를 어중간하게 만들었다. 다음엔 선택과 집중을 한다.
한 줄 정리: 성과는 개인 프로젝트 한 곳에서 났지만, 본질이었던 팀 프로젝트도 개인 공부도 어중간했다. 다음엔 일을 벌이기보다 끝까지 가시화하고, AI가 짜준 코드를 이해한 뒤 넘어가겠다.
마치며
이번 프로젝트는 화려한 출시 성과보다, '어떻게 팀으로 합의하고 설계하는가', 그리고 '나는 어디에 집중했어야 했는가(우선 순위 설정)' 를 동시에 배운 시간이었습니다. DDD가 가르쳐 준 책임 분리의 감각, 그리고 욕심을 줄이고 코드를 이해하며 가야 한다는 반성 — 두 가지는 다음 프로젝트의 첫 번째 체크리스트가 될 것입니다.
기술 스택: Next.js (FE) · Spring 기반 (BE) · JWT · DDD · Figma
기간: 2026.05.28 ~ 06.25 (4주) · 팀 LCS(좋댓구)