댓글, 신고 테이블 설계
댓글, 신고 테이블 설계
main
date
Aug 17, 2025
slug
comment-report-schema
author
status
Public
tags
summary
댓글, 신고 테이블 설계
type
Post
thumbnail
category
Development
updatedAt
Apr 14, 2026 09:44 PM
댓글 테이블
''' 댓글 타입 코드 ''' class CommentType(models.IntegerChoices): INTEGRATED_CHART = 1 # 통합 차트 댓글 #SONG_DETAIL = 2 노래 상세 확장 고려 #ARTIST_DETAIL = 3 아티스트 상세 확장 고려 class IchartComment(models.Model): id = models.AutoField(primary_key=True) # PK comment_type = models.IntegerField(choices=CommentType.choices, default=CommentType.INTEGRATED_CHART) # 댓글 타입 (통합 차트, 곡 상세, 아티스트 상세 등등 확장 고려) # 대상 테이블 정보 (통합 차트, 곡 상세, 아티스트 상세 등등 확장 고려) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True) object_id = models.PositiveIntegerField(null=True, blank=True) target_id = GenericForeignKey("content_type", "object_id") # 댓글 계층 parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)# 부모 댓글 번호 depth = models.IntegerField() # 계층 (대댓글 고려) reply_count = models.IntegerField(default=0) member_id = models.ForeignKey(IchartMember, on_delete=models.CASCADE, null=True) # 회원 번호 content = models.TextField() # 댓글 내용 ip = models.CharField(max_length=45) # 아이피 created_at = models.BigIntegerField(default=get_unix_timestamp) # 생성 시간 updated_at = models.BigIntegerField(null=True, blank=True) # 수정 시간 deleted_at = models.BigIntegerField(null=True, blank=True) # 삭제 시간 class Meta: managed = True db_table = 'ichart_comment' indexes = [ models.Index(fields=['parent', 'created_at'], name='idx_ichart_comment_parent'), # 부모 기준 날짜 정렬 models.Index(fields=['content_type', 'created_at'], name='idx_ichart_comment_content_type') # 컨텐츠 타입에 따라 검색 기준 정렬 ]
현재는 통합 차트 댓글만 있지만 추후에 곡, 아티스트, 앨범 등 댓글이 붙을 수 있는 페이지 확장을 고려해야함
또한 회원의 경우 비회원 댓글이 가능하기 때문에 nullable
(현재 회원 시스템이 없기 때문에 껍데기 테이블 추가 - 추후 별도 로그인 제공)
신고 테이블
from django.db import models from common.utils.common_utils import get_unix_timestamp from common.constants.status_codes import ReportStatus, ReportReasonCode class ReportPlatform(models.IntegerChoices): INSTIZ = 1, "인스타즈" ICHART = 2, "아이차트" ''' 신고 상태 코드 ''' class ReportStatus(models.IntegerChoices): PENDING = 0, "미처리" REVIEWED = 1, "검토완료" REJECTED = 2, "거절" PROCESSED = 3, "처리완료" DELETED = 4, "삭제" ''' 인스티즈 1 만 번 대 번호 아이차트 2 만 번 대 번호 TODO 아이차트 우선 작업 ''' class ReportReasonCode(models.IntegerChoices): ICHART_SONG_DETAIL_REPORT = 20001, "아이차트 곡 상세 정보 신고" ICHART_ARTIST_DETAIL_REPORT = 20002, "아이차트 아티스트 상세 정보 신고" ICHART_MUSIC_VIDEO_REPORT = 20003, "아이차트 뮤직 비디오 정보 신고" # 신고 그룹 class ReportGroup(models.Model): id = models.AutoField(primary_key=True) group_id = models.IntegerField() # 그룹 아이디 parent = models.ForeignKey( 'self', null=True, blank=True, on_delete=models.CASCADE, related_name='children' ) platform = models.IntegerField(choices=ReportPlatform.choices, default=ReportPlatform.INSTIZ) # 신고 플랫폼 name = models.CharField(max_length=200) # 신고 라벨 description = models.TextField(null=True, blank=True) category = models.IntegerField() # 신고 아이템 카테고리 번호 created_at = models.BigIntegerField(default=get_unix_timestamp) updated_at = models.BigIntegerField(null=True, blank=True) class Meta: managed = True db_table = 'instiz_report_group' indexes = [ models.Index(fields=['group_id'], name='idx_instiz_report_group_id') ] class InstizReport(models.Model): id = models.AutoField(primary_key=True) # PK report_group = models.ForeignKey(ReportGroup, on_delete=models.CASCADE, null=True, blank=True) # 신고 그룹 target_id = models.IntegerField(null=True, blank=True) # 신고 대상 아이디 target_type = models.CharField(max_length=200, null=True, blank=True) # 카테고리 아이디 (song, artist, music_video) reporter_id = models.IntegerField(null=True, blank=True) # 신고자 아이디 (비회원의 경우 정보가 없음) reason_code = models.IntegerField(choices=ReportReasonCode.choices, default=None, null=True, blank=True) # 신고 사유 reason_detail = models.TextField(null=True, blank=True) # 신고 사유 상세 status = models.IntegerField(choices=ReportStatus.choices, default=ReportStatus.PENDING) # 신고 상태 created_at = models.BigIntegerField(default=get_unix_timestamp) # 생성 시간 reviewed_at = models.BigIntegerField() # 검토 시간 reviewed_id = models.IntegerField() # 검토자 아이디 class Meta: managed = True db_table = 'instiz_report' indexes = [ models.Index(fields=['target_type', 'target_id'], name='idx_instiz_report_target'), # 대상 타입, 대상 아이디 기준 검색 models.Index(fields=['status', 'created_at'], name='idx_instiz_report_status'), # 상태 ]
신고 항목, 신고 내용, 파일 업로드 테이블 분리
신고 항목 (group)의 경우 신고 내용 리스트가 들어있고, 이 pk를 신고 내용 리스트의 fk 로써 존재하게 하여 신고 내용 추론 가능하도록
아이차트와 인스티즈 신고 목록이 정의된게 없다. 그래서 우선 아이차트의 경우 신고 번호를 2만번대를 부여, 인스티즈의 신고 번호를 1만대로 부여하고
1 00 00 형태로 각 항목 구분 (이렇게 할 경우 100 번대는 사용하지 못하는데…. 그렇게 까지 항목이 많지는 않으니까 괜찮지 않을까 싶은데)
파일 테이블
class InstizUploadFile(models.Model): id = models.AutoField(primary_key=True) target_type = models.IntegerField(choices=InstizUploadFileTargetType.choices, default=InstizUploadFileTargetType.REPORT) target_id = models.IntegerField() # target_type 대상 id file_key = models.CharField(max_length=200, unique=True) ## UUID file_name = models.CharField(max_length=200) file_path = models.CharField(max_length=1000) file_size = models.IntegerField() file_extension = models.CharField(max_length=200) created_at = models.BigIntegerField(default=get_unix_timestamp) updated_at = models.BigIntegerField(null=True, blank=True) class Meta: managed = True db_table = 'instiz_uploaded_file' indexes = [ models.Index(fields=['target_type', 'target_id'], name='idx_instiz_uploaded_file_target'), ] def save(self, *args, **kwargs): if not self.file_key: self.file_key = str(uuid.uuid4()) if self.pk is not None: self.updated_at = get_unix_timestamp() super().save(*args, **kwargs)
신고 시 넘겨받는 파일(스크린샷)의 경우 별도 파일 테이블로 분리하고 type과 id를 통하여 파일 경로 얻어올 수 있도록
레거시 댓글 테이블 정보
class ZetyxBoardCommentSpage(models.Model): no = models.AutoField(primary_key=True) parent = models.IntegerField() # spage number reple_parent = models.IntegerField(db_column='replemode',default=0, help_text="댓글 부모코드") reple_count = models.IntegerField(db_column='reple',default=0, help_text="답글 카운트") ismember = models.IntegerField(default=0) level = models.FloatField(default=10) name = models.CharField(max_length=40) prehtml = models.CharField(max_length=200) precomment = models.CharField(max_length=200) memo = models.TextField() ip = models.CharField(max_length=45) reg_date = models.IntegerField(default=get_unix_timestamp) image = models.CharField(max_length=250) sms = models.IntegerField() newalert = models.IntegerField() class Meta: managed = False db_table = '-'
- parent
- 게시글 no
- spage 항목에서는 spage 번호
- replemode
- 다답글 했을 때 상위 댓글 no
- reple
- 해당 댓글의 답글 개수
- isMember
- 회원 아이디 (비회원의 경우 0 으로 들어감
- level
- 회원 레벨 같다 이거
- name
- 닉네임
- prehtml
- ??
- precomment
- 닉네임 앞에 붙는거 (터치드의 샛별이)
- 닉네임 (타이틀) 형태로 붙어짐
- memo
- 본문
- ip
- 작성 ip
- reg_date
- 등록일
- image
- gif(tenor api), 파일 이미지 입력 시 저장
- sms
- ????
- newalert
- ????
→ 게시판 카테고리 별로 comment 테이블 별도 존재
기본적으로 필요한 컬럼
- 댓글id (PK)
- 댓글 내용
- 댓글 그룹 (부모 0, 아래 댓글들은 1~ )
- 부모 댓글 id (계층일 경우 부모 id)
- 회원 id
- 회원 유무 (회원 | 비회원) ?
- 게시글 id
- ip
- 댓글계층
- 생성일
- 수정일
- 삭제일
- id
- 댓글 PK
- post_id
- 게시글 id FK
- parent_id
- 부모 댓글 id
- group_depth
- 댓글 계층 (default 0)
- user_id
- 회원 id (fk, 비회원 null)
- is_guest
- 회원 비회원 여부
- content
- ip
- created_at
- updated_at
- deleted_at
- id
- 신고 pk
- target_type
- enum으로 관리?? 게시글, 댓글 등등
- target_id
- 신고 대상 pk id
- reporter_id
- 신고자 (회원) id
- reason_code
- 신고 코드
- reason_detail
- 상세 신고 내용
- status
- 신고 처리 상태
- created_at
- reviewed_at
- reviewed_id
- 신고 처리한 사람 id
report_file
- id
- 파일 pk
- report_id
- 신고 id
- file_path_url
- 이미지 파일
- file_type
- 파일 유형
- created_at
→ report 생성 후 파일 서버 업로드 → 파일 경로 얻어서 report_file 저장
→ 트랜잭션 묶어서 관리