Published on

게시판 카테고리 설계 — 단일 모델 + category 필드 vs 앱 분리

Authors
  • avatar
    Name
    Hyo814
    Twitter

게시판 카테고리 설계 — 단일 모델 + category 필드 vs 앱 분리

카페 커뮤니티를 만들면서 게시판을 8개 카테고리로 쪼개야 했습니다. 후기, 구인, 양도, 창작, 건의, 팁, 모집, 온라인. 이걸 어떻게 모델링할지 꽤 오래 고민했고, 결국 단일 Post 모델 + category 필드 로 갔습니다. 그 과정의 트레이드오프를 정리합니다.


1. 세 가지 선택지

게시판 카테고리를 설계할 때 흔히 나오는 선택지는 세 가지입니다.

선택지 A — 카테고리별로 앱 분리

review/    models.py -> ReviewPost
job/       models.py -> JobPost
ticket/    models.py -> TicketPost
...

선택지 B — 단일 앱 + 카테고리별 서브클래스 (multi-table inheritance 또는 proxy)

class Post(models.Model):  # 공통
    ...

class JobPost(Post):  # multi-table inheritance
    salary = models.IntegerField()

선택지 C — 단일 앱 + 단일 모델 + category 필드

class Post(models.Model):
    CATEGORY_CHOICES = [
        ('review', '후기'),
        ('job', '구인'),
        ...
    ]
    category = models.CharField(max_length=20, choices=CATEGORY_CHOICES)
    title = models.CharField(max_length=200)
    content = models.TextField()
    ...

2. 평가 기준 — 뭘 중요하게 볼 것인가

단순히 "어느 게 깔끔하냐" 가 아니라, 이번 프로젝트에 맞는 기준을 먼저 잡았습니다.

  1. 카테고리별 필드 차이가 얼마나 큰가?
  2. 전체 검색(카테고리 무관)이 필요한가?
  3. 카테고리 추가/삭제 빈도가 높은가?
  4. URL 구조가 카테고리별로 달라야 하는가?
  5. 권한이나 정책이 카테고리별로 크게 다른가?

이 다섯 개 기준으로 선택지를 하나씩 때려봤습니다.


3. 이번 프로젝트의 답

기준이번 프로젝트유리한 선택지
카테고리별 필드 차이거의 없음 (전부 제목/본문/작성자)C
전체 검색 필요있음 (헤더 검색창)C
카테고리 추가/삭제 빈도낮음 (8개 고정 예정)A, B, C 모두 가능
URL 구조/board/<category>/ 로 통일C
권한/정책 차이미미 (관리자 공지 정도)C

다섯 중 네 개가 C로 몰렸습니다. 그래서 단일 모델 + category 필드로 갔습니다.


4. A(앱 분리)의 함정 — 중복의 비용

선택지 A는 처음에 가장 "깔끔해 보이는" 선택입니다. 앱 구조가 카테고리와 1:1로 맞으니 직관적이죠. 그런데 실제로 써보면 이런 일들이 생깁니다.

  • 공통 템플릿(글 목록, 상세, 작성)을 8번 복붙
  • 공통 뷰 로직(페이지네이션, 권한 체크)도 8번 복붙
  • 전체 검색을 구현하려면 8개 모델을 UNION ALL 로 합쳐야 함
  • 공통 기능 추가(예: 좋아요, 북마크)가 모든 앱을 건드리는 작업이 됨

카테고리별 필드가 정말 크게 다르지 않다면, 이 중복은 빠르게 기술 부채가 됩니다. 반대로 "구인" 카테고리에만 급여, 근무지, 연락처 같은 고유 필드가 한가득 있다면 A가 정답입니다. 그런데 이 프로젝트는 그런 상황이 아니었습니다.


5. B(상속)의 함정 — ORM 복잡도

선택지 B는 "공통은 Post에 두고, 카테고리별 필드는 서브클래스에" 라는 깔끔한 OOP 접근입니다. Django에서는 이걸 multi-table inheritance로 지원합니다.

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()

class JobPost(Post):
    salary = models.IntegerField()

단점:

  • JOIN이 기본입니다. JobPost.objects.all()PostJobPost 테이블을 조인합니다. 작을 때는 괜찮지만 테이블이 커지면 성능에 영향.
  • 전체 글 목록을 얻으려면 Post.objects.all() 을 돌리는데, 이때 서브클래스 필드는 접근할 수 없음. 필요하면 select_related 로 끌어와야 합니다.
  • 마이그레이션이 번잡. 서브클래스 모델 추가마다 FK가 새로 생성.

OOP적으로는 예쁘지만, Django ORM이 실제로 돌아가는 방식과 정합성이 떨어집니다. "이론적으로 맞는" 설계와 "현실적으로 편한" 설계가 엇갈리는 지점이죠.


6. C(단일 모델)의 트레이드오프 — 인정하고 가져간 것

단일 모델을 택했을 때 내가 감수하겠다고 결정한 단점도 분명히 있었습니다.

6.1 필드 분기가 생기면 지저분해진다

나중에 "구인 카테고리만 급여 필드를 받고 싶다" 는 요구가 생기면, 단일 모델에서는 extra_data(JSON 필드)나 별도 테이블로 도망가야 합니다. 이때가 바로 B나 A로 마이그레이션해야 하는 시점 입니다.

6.2 카테고리 목록이 코드에 박혀 있다

CATEGORY_CHOICES 가 상수로 박혀 있어서, 새 카테고리 추가하려면 코드 수정 + 마이그레이션이 필요합니다. 카테고리를 런타임에 바꿀 수 있으려면 별도 Category 테이블로 빼야 합니다.

이번에는 카테고리를 자주 안 바꿀 거라 상수로 두는 게 편했습니다. 관리자가 직접 카테고리를 추가하는 서비스였다면 테이블로 뺐을 겁니다.

6.3 URL 라우팅 한 곳에서 전부 분기

# board/urls.py
urlpatterns = [
    path('<str:category>/', views.board_list, name='list'),
    path('<str:category>/write/', views.board_write, name='write'),
    path('<str:category>/<int:pk>/', views.board_detail, name='detail'),
]

URL 한 곳에 카테고리가 들어가고, 뷰에서 category를 받아 필터링합니다. 유효하지 않은 카테고리는 404로 보냅니다.

VALID_CATEGORIES = dict(Post.CATEGORY_CHOICES)

def board_list(request, category):
    if category not in VALID_CATEGORIES:
        raise Http404()
    posts = Post.objects.filter(category=category)
    ...

7. "확장성" 의 유혹에 지지 않기

설계할 때 가장 흔한 실수가 "나중에 바뀔 수도 있으니 지금 유연하게 만들자" 입니다. 8개 카테고리가 80개가 될 수도 있고, 각 카테고리가 완전히 다른 필드를 가질 수도 있고… 이런 "혹시" 를 전부 대비하면 YAGNI 원칙을 위반한 오버엔지니어링이 됩니다.

이번 프로젝트의 기준:

  • 카테고리가 정말로 런타임에 추가돼야 하는가? → 아니요
  • 카테고리별 필드가 정말로 크게 다른가? → 아니요
  • 운영 중에 스키마가 정말로 자주 바뀔 것 같은가? → 아니요

세 개 다 "아니요" 면 단일 모델이 맞습니다. 나중에 정말로 필요해지면 그때 마이그레이션하면 됩니다. "나중의 가능성"을 위해 지금의 복잡도를 쓰지 않는다.


8. 체크리스트 — 단일 모델이 적합한지 판정

다음 프로젝트에서도 쓸 수 있도록 체크리스트로 정리했습니다.

  • 카테고리별 공통 필드 비율이 80% 이상인가?
  • 전체 검색(카테고리 무관)이 자주 필요한가?
  • 카테고리 추가 빈도가 낮은가 (연 1~2회 이하)?
  • URL 구조가 카테고리 파라미터 하나로 충분한가?
  • 카테고리별 권한 정책이 동일하거나 간단한 분기로 처리 가능한가?

4개 이상 예스면 단일 모델이 편합니다. 2개 이하면 앱 분리를 검토할 타이밍입니다.


정리

  • 필드 차이가 작고 공통 화면이 많은 게시판은 단일 모델 + category 필드가 제일 싸고 단순
  • 상속(B) 은 OOP적으로 예쁘지만 ORM 복잡도가 늘어 실익이 적음
  • 앱 분리(A) 는 카테고리별 필드 차이가 클 때만
  • "나중의 확장성" 을 핑계로 지금 복잡도를 키우지 않는다
  • 단일 모델의 한계(필드 분기, 런타임 카테고리)가 실제로 닥치면 그때 마이그레이션