어드민을 서비스 도메인에서 격리하기
어드민 API는 조회, 수정, 릴리스 위험이 다릅니다. 운영 편의가 핵심 서비스를 바꾸지 않도록 모듈이나 저장소로 격리합니다.
출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다.
gpt-5.6-sol모델을 사용해 생성·편집했습니다.
저는 보통 어드민 기능을 서비스 도메인으로 보지 않습니다. 제품 주변에서 운영을 위해 사용하는 영역이고, 사용자와 조회 방식, 수정 요구가 제품 기능과 다릅니다.
중요한 예외가 있습니다. 제품 자체가 어드민 제품이라면 어드민이 당연히 도메인입니다. 고객에게 어드민 플랫폼을 제공하는 팀은 그 일을 중심 개념으로 모델링해야 합니다. 제가 말하려는 것은 리뷰, 쿠폰, 계정 같은 제품을 맡은 팀이 운영자나 전사 어드민 팀에 내부 API도 제공하는 흔한 경우입니다.
이 상황에서 어드민 동작을 핵심 도메인 안에 넣으면 가장 안정적으로 유지해야 할 코드가 오염되기 쉽습니다.
어드민과 제품 코드는 바뀌는 이유가 다르다
고객 대상 서비스는 제품 규칙에 따라 상태 변경을 제한합니다. 어드민 도구는 운영자가 데이터를 교정해야 해서 더 넓은 수정 기능을 요구할 수 있습니다. 두 영역이 엔티티 하나를 공유하면 어드민 작업에 필요한 setter와 수정 경로가 서비스 코드에도 열립니다.
읽기에서도 같은 불일치가 생깁니다. 어드민 사용자는 작성자, 작성 시간, 상태와 여러 정렬 조건을 조합해 검색하고 싶어 합니다. 모두 타당한 운영 요구사항입니다. 그렇다고 자동으로 핵심 도메인 동작이 되는 것은 아닙니다. 모든 검색 조합을 도메인 리포지토리에 넣으면 내부 화면 변화에 맞춰 중심 모델이 계속 커집니다.
처음에는 기존 리뷰 조회에 조건 하나만 추가하는 편이 경계를 만드는 것보다 싸 보입니다. 이후 페이지네이션, 선택 필터, 새로운 정렬, 데이터 교정 기능이 쌓입니다. 어드민 화면이 바뀔 때마다 서비스 중심부를 고칩니다. 이 흐름을 여러 번 겪었기 때문에 방향이 분명하다면 비교적 일찍 격리하는 편을 선호합니다.
상황에 맞는 격리 강도 고르기
여러 팀이 함께 쓰는 전사 어드민이라면 별도 저장소와 애플리케이션을 진지하게 검토합니다. 이미 소유권과 릴리스 주기가 다르기 때문입니다.
작은 회사에서는 사용자가 적은 내부 도구 때문에 서버를 하나 더 운영하기 부담스러울 수 있습니다. 그러면 프로젝트와 실행 애플리케이션은 하나로 유지하되 어드민 API 모듈을 분리할 수 있습니다. 모듈이 두 개라고 서버 프로세스도 반드시 두 개일 필요는 없습니다. 런타임 하나가 서비스 API와 어드민 API 모듈을 함께 포함하면서, 빌드 구조로 어드민 코드가 사용하면 안 되는 내부 구현에 접근하지 못하게 할 수 있습니다.
중요한 것은 배포 개수의 유행이 아니라 격리입니다. 어드민 모듈의 변경은 어드민 변경으로 선명하게 남아야 합니다. 코어 코드까지 바뀌었다면 PR에서 예외가 눈에 띄고 리뷰어가 이유를 물을 수 있어야 합니다.
어드민 영역이 커지면 잘 격리된 모듈을 별도 서버나 저장소로 옮기기도 쉽습니다. 작은 내부 화면으로 시작한 어드민이 전사 운영 시스템으로 커지는 경우가 있기 때문에 이 선택지를 남기는 데 의미가 있습니다.
한 테이블에 엔티티 두 개도 가능하다
한 예제 구조에서는 어드민 API가 서비스 엔티티와 같은 테이블을 사용하면서도 자체 JPA 엔티티 매핑을 가집니다. 테이블 하나에 엔티티 두 개라 기형적으로 보일 수 있습니다.
의도적인 중복입니다. 서비스 매핑은 고객 대상 비즈니스 규칙에서 허용한 변경만 노출합니다. 어드민 매핑은 운영자가 교정할 수 있는 필드를 노출합니다. 변경 가능한 엔티티 하나를 공유하면 양쪽에서 필요한 기능의 합집합을 모두 허용해야 합니다.
모든 엔티티를 복제하라는 일반 규칙은 아닙니다. 실수로 경계를 넘었을 때의 위험이 중복 비용을 정당화하는 경우에 쓰는 강한 장치입니다. 매핑을 두 개 유지하면 테이블 변경 때 두 곳을 관리해야 합니다. 대신 컴파일러와 모듈 그래프가 서비스 개발자가 어드민 전용 수정을 가볍게 가져다 쓰는 일을 막습니다.
저는 코드 리뷰에서 작은 setter 실수를 반복해서 잡는 데 시간을 쓰기보다 기계적으로 막는 편을 선호합니다. 리뷰에서는 비즈니스와 위험한 예외를 이야기해야 합니다. 필드마다 내부 화면에서만 수정 가능한지 모든 리뷰어의 기억에 맡기고 싶지 않습니다.
모듈마다 내부 규칙이 달라도 된다
격리하면 어드민 모듈이 도메인 모듈과 다르게 보인다는 걱정이 생깁니다. 저는 그 차이가 맞을 수 있다고 봅니다. 어드민 코드에는 같은 구현 계층이나 풍부한 개념 객체가 필요하지 않을 수 있습니다. 빠르고 직접적인 조회와 단순한 운영 명령이 더 적합할 수 있습니다.
각 모듈 안에서는 통일성을 지켜야 합니다. README에 계층 구조, 허용 의존성, 느슨한 규칙을 짧게 적습니다. 개발자가 어드민 영역을 미완성 코어 코드라고 오해하지 않고 왜 더 단순하거나 조회 중심인지 이해할 수 있어야 합니다.
이름도 반드시 admin-api일 필요는 없습니다. 서비스를 운영하는 여러 도구를 포함한다면 operations가 책임을 더 잘 설명할 수 있습니다. 이름은 이 조직에서 맡은 일을 나타내면 됩니다.
경계가 리뷰와 릴리스를 더 안전하게 만든다
개발자가 어드민 기능만 고친 PR이라고 말한다고 해보겠습니다. 어드민과 서비스 코드가 섞여 있으면 리뷰어는 코어 패키지 전체의 변경을 살피며 고객에게 영향을 주는지 판단해야 합니다. 시스템을 충분히 이해하지 못한 사람이 한 작은 실수가 서비스 장애로 나갈 수 있습니다.
모듈이 나뉘어 있으면 diff 자체가 신호가 됩니다. 어드민 모듈 안의 변경은 작업 범위와 맞습니다. 코어 변경이 있다면 설명이 필요합니다. 경계가 안전을 보장하지는 않지만 리뷰어가 의심해야 할 범위를 줄입니다.
내부 운영 도구라면 저는 보통 다음 방향에서 시작합니다.
- 어드민 동작을 핵심 도메인 밖에 둡니다.
- 런타임 하나가 현실적인 한계라면 별도 모듈을 사용합니다.
- 어드민 편의 기능이 서비스 코드로 새지 않도록 의존성을 강하게 제한합니다.
- 소유권과 영향도가 커지면 별도 저장소나 서버로 옮깁니다.
- 모듈마다 규칙이 다른 이유를 문서화합니다.
정답은 조직에서 어드민이 무엇을 뜻하느냐에 따라 달라집니다. 어드민 제품, 작은 팀의 데이터 교정 화면, 전사 운영 플랫폼은 같은 아키텍처 문제가 아닙니다. 먼저 어떤 어드민인지 정의해야 합니다. 그다음 잦은 운영 변경이 고객 대상 서비스를 더 이해하기 어렵고 깨지기 쉽게 만들지 않을 만큼 격리하면 됩니다.