sirsoft-board 댓글·대댓글 깊이 및 무결성 버그 보고
요약
sirsoft-board의 댓글·대댓글 처리에서 다음 세 가지 문제가 확인됩니다.
| 구분 |
문제 |
영향 |
| 대댓글 깊이 |
max_comment_depth는 최대 10이지만 저장 depth가 5로 고정됨 |
6단계 이후 깊이 제한 무력화 및 표시 계층 왜곡 |
| 부모 댓글 무결성 |
parent_id가 같은 게시글의 댓글인지 검증하지 않음 |
다른 게시글 댓글을 부모로 가진 고아 댓글 생성 가능 |
| REST 경로 무결성 |
댓글 수정·삭제에서 URL의 postId를 사용하지 않음 |
다른 게시글 경로로 댓글 수정·삭제 가능 |
가장 우선도가 높은 문제는 첫 번째입니다. 기본 max_comment_depth가 10이므로 별도 설정을 변경하지 않은 게시판도 영향을 받습니다.
확인 환경
| 항목 |
값 |
| 그누보드 버전 |
7.0.4 |
| 게시판 모듈 |
sirsoft-board 1.2.0 |
| 확인 커밋 |
348efa2e98df22b00d30d3a4e267d0305f201758 |
| 확인일 |
2026-07-17 |
관련 원본 파일에 로컬 변경이 없는 상태에서 분석했습니다.
이슈 1. max_comment_depth 6~10에서 depth가 5로 고정되고 제한이 무력화됨
제안 제목
[sirsoft-board] max_comment_depth 6~10 설정에서 depth가 5로 고정되고 대댓글 제한이 무력화됨
문제 정의
게시판 설정과 DB 스키마는 대댓글 깊이를 최대 10까지 지원하지만, 댓글 생성 서비스는 계산된 depth를 숫자 5로 강제 제한합니다.
설정 기본값:
근거: modules/_bundled/sirsoft-board/config/settings/defaults.json:18
설정 허용 범위:
'max_comment_depth_min' => 0,
'max_comment_depth_max' => 10,
근거: modules/_bundled/sirsoft-board/config/board.php:150-152
DB 기본값:
$table->unsignedSmallInteger('max_comment_depth')
->default(10)
->comment('대댓글 최대 깊이 (1~10)');
근거: modules/_bundled/sirsoft-board/database/migrations/2026_04_01_000002_create_boards_table.php:46
실제 댓글 생성 코드:
// 부모 댓글의 depth + 1 (최대 5까지)
$data['depth'] = min(($parentComment->depth ?? 0) + 1, 5);
근거: modules/_bundled/sirsoft-board/src/Services/CommentService.php:252-257
깊이 검증 코드:
if ($parentComment->depth + 1 > $board->max_comment_depth) {
$fail(__('sirsoft-board::validation.comment.depth.exceeded', [
'max' => $board->max_comment_depth,
]));
}
근거: modules/_bundled/sirsoft-board/src/Rules/CommentValidationRule.php:113-116
재현 절차
- 대댓글 사용이 활성화된 게시판의
max_comment_depth를 10으로 설정합니다.
- 게시글에 최상위 댓글을 작성합니다.
- 직전에 작성한 댓글 ID를 다음 요청의
parent_id로 지정합니다.
- 동일한 방식으로 11단계 이상 대댓글을 연속 작성합니다.
- 댓글 생성 응답 또는 댓글 목록 API에서 각 댓글의
depth를 확인합니다.
사용자 댓글 생성 API:
POST /api/modules/sirsoft-board/boards/{slug}/posts/{postId}/comments
Content-Type: application/json
{
"content": "대댓글 깊이 확인",
"parent_id": 123
}
라우트 근거: modules/_bundled/sirsoft-board/src/routes/api.php:479-488
예상 결과
| 대댓글 단계 |
저장 depth |
응답 |
| 1~10 |
1~10 |
201 Created |
| 11 |
저장하지 않음 |
422 Unprocessable Entity |
실제 결과
현재 코드 수식을 그대로 적용하면 다음과 같이 처리됩니다.
| 대댓글 단계 |
부모 depth |
검증 |
저장 depth |
| 1 |
0 |
통과 |
1 |
| 2 |
1 |
통과 |
2 |
| 3 |
2 |
통과 |
3 |
| 4 |
3 |
통과 |
4 |
| 5 |
4 |
통과 |
5 |
| 6 |
5 |
통과 |
5 |
| 7 이상 |
5 |
계속 통과 |
계속 5 |
max_comment_depth=10일 때 부모 depth가 5이면 검증식은 항상 5 + 1 > 10이 되어 실패하지 않습니다. 생성 서비스는 다시 min(6, 5)를 저장하므로 이후 부모 댓글도 계속 depth=5입니다.
사용자 화면 영향
사용자 템플릿은 API의 depth만큼 들여쓰기하고, 답글 버튼도 depth < max_comment_depth 조건으로 표시합니다.
"marginLeft": "{{Math.min(comment?.depth ?? 0, 10) * 1}}rem"
"if": "{{comment?.abilities?.can_write && (comment?.depth ?? 0) < (post?.data?.board?.max_comment_depth ?? 10) ...}}"
근거: templates/_bundled/sirsoft-basic/layouts/partials/board/show/_comment_item.json:11, :121
따라서 6단계 이후 댓글은 계속 5단계로 표시되면서 답글 버튼은 계속 노출됩니다.
성능 영향
댓글 목록은 부모·자식 정렬과 자손 댓글 수 계산을 재귀적으로 수행합니다.
근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:160-195
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:206-235
깊이 제한이 무력화된 긴 단일 체인에서는 재귀 깊이가 증가합니다. 또한 모든 댓글에서 다시 전체 자손을 계산하므로 긴 체인일수록 CPU 비용이 커질 수 있습니다.
테스트 누락
현재 경계 테스트는 max_comment_depth=2만 사용합니다. 서비스에 하드코딩된 5를 넘지 않기 때문에 이 문제를 검출하지 못합니다.
근거: modules/_bundled/sirsoft-board/tests/Feature/User/CommentCrudScenarioTest.php:189-205
수정 방향
$data['depth'] = ($parentComment->depth ?? 0) + 1;
생성 서비스의 숫자 5 하드코딩을 제거하고, 게시판별 최대 깊이 초과는 기존 CommentValidationRule에서 거부하는 방식이 적절합니다. 저장 단계에서도 방어적으로 계산된 깊이가 게시판 설정을 초과하지 않는지 확인할 수 있습니다.
완료 조건
max_comment_depth=10에서 depth=1~10이 정확히 저장됩니다.
depth=10 댓글에 답글을 작성하면 422를 반환합니다.
max_comment_depth=0, 1, 5, 6, 10 경계 테스트가 추가됩니다.
- 저장된
depth와 실제 parent_id 체인의 깊이가 일치합니다.
- 사용자 및 관리자 댓글 생성 API에 동일한 규칙이 적용됩니다.
이슈 2. 다른 게시글 댓글을 parent_id로 지정할 수 있음
제안 제목
[sirsoft-board] 대댓글 parent_id가 동일 게시글 소속인지 검증하지 않아 고아 댓글이 생성됨
문제 정의
대댓글 생성 요청은 URL의 postId를 post_id로 사용하지만, parent_id 검증은 부모 댓글의 board_id만 확인합니다. 부모 댓글의 post_id가 현재 게시글과 같은지 확인하지 않습니다.
요청 검증:
'post_id' => ['bail', 'required', 'integer', new CommentValidationRule($slug, 'post')],
'parent_id' => [
'nullable',
'integer',
new CommentValidationRule($slug, 'parent_comment'),
],
근거: modules/_bundled/sirsoft-board/src/Http/Requests/StoreCommentRequest.php:75-80
부모 댓글 조회:
$parentComment = $board
? Comment::where('board_id', $board->id)->withTrashed()->find($parentId)
: null;
근거: modules/_bundled/sirsoft-board/src/Rules/CommentValidationRule.php:88-93
Repository 조회도 게시판과 댓글 ID만 사용합니다.
근거: modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:259-267
재현 절차
- 동일 게시판에 게시글 A와 게시글 B를 생성합니다.
- 게시글 A에 댓글 A-1을 작성합니다.
- 게시글 B의 댓글 생성 API에 댓글 A-1의 ID를
parent_id로 전달합니다.
POST /api/modules/sirsoft-board/boards/{slug}/posts/{postBId}/comments
Content-Type: application/json
{
"content": "게시글 B에 작성하지만 부모는 게시글 A의 댓글",
"parent_id": "{commentA1Id}"
}
예상 결과
부모 댓글이 현재 게시글 소속이 아니므로 422 Unprocessable Entity를 반환해야 합니다.
현재 코드 경로
같은 게시판의 유효한 댓글이므로 부모 댓글 검증을 통과합니다. 생성되는 행은 post_id=게시글 B, parent_id=게시글 A의 댓글 조합을 가질 수 있습니다.
댓글 목록 조회는 현재 게시글의 post_id로만 댓글을 가져오고 parent_id=null인 댓글부터 트리를 구성합니다.
근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:45-48
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:160-175
따라서 게시글 B에 저장된 자식 댓글은 부모가 조회 결과에 없어 화면에서 누락될 수 있습니다.
추가 영향
- 부모 댓글의
replies_count가 다른 게시글의 대댓글로 증가할 수 있습니다.
- 게시글 B의 정보와 게시글 A 댓글 작성자를 조합한 잘못된 답글 알림이 발송될 수 있습니다.
- DB는 파티션 구조 때문에
parent_id 외래키가 없으므로 애플리케이션 검증 누락이 그대로 데이터 무결성 훼손으로 이어집니다.
근거:
modules/_bundled/sirsoft-board/src/Listeners/CommentReplySyncListener.php:47-54
modules/_bundled/sirsoft-board/src/Listeners/BoardNotificationDataListener.php:181-206
modules/_bundled/sirsoft-board/database/migrations/2026_04_01_000005_create_board_comments_table.php:28
테스트 누락
현재 테스트는 존재하지 않는 parent_id만 확인합니다. 같은 게시판의 다른 게시글에 존재하는 댓글 ID는 테스트하지 않습니다.
근거: modules/_bundled/sirsoft-board/tests/Feature/User/CommentCrudScenarioTest.php:208-221
완료 조건
- 부모 댓글의
board_id와 post_id가 현재 요청 대상과 모두 일치해야 합니다.
- 같은 게시판의 다른 게시글 댓글을
parent_id로 전달하면 422를 반환해야 합니다.
- 사용자·관리자 댓글 생성 API에 동일한 검증이 적용되어야 합니다.
- 잘못된 부모 관계에서는 댓글, 답글 수 및 알림 후처리가 실행되지 않아야 합니다.
이슈 3. 댓글 수정·삭제에서 URL postId를 검증하지 않음
제안 제목
[sirsoft-board] 댓글 수정·삭제 API가 URL의 postId와 댓글 소속 post_id 일치를 검증하지 않음
문제 정의
사용자와 관리자 댓글 수정·삭제 컨트롤러는 $postId를 인자로 받지만 댓글 조회 및 수정·삭제 과정에서 사용하지 않습니다.
사용자 API 근거:
modules/_bundled/sirsoft-board/src/Http/Controllers/User/CommentController.php:119-149
modules/_bundled/sirsoft-board/src/Http/Controllers/User/CommentController.php:165-195
관리자 API 근거:
modules/_bundled/sirsoft-board/src/Http/Controllers/Admin/CommentController.php:121-144
modules/_bundled/sirsoft-board/src/Http/Controllers/Admin/CommentController.php:160-179
댓글 Repository는 board_id + commentId로만 댓글을 찾습니다.
근거: modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:279-287
재현 절차
- 동일 게시판에 게시글 A와 B를 생성합니다.
- 게시글 A에 본인 댓글 A-1을 작성합니다.
- 게시글 B의 URL에 댓글 A-1 ID를 넣어 수정 또는 삭제를 요청합니다.
PUT /api/modules/sirsoft-board/boards/{slug}/posts/{postBId}/comments/{commentA1Id}
DELETE /api/modules/sirsoft-board/boards/{slug}/posts/{postBId}/comments/{commentA1Id}
예상 결과
댓글이 URL의 게시글에 속하지 않으므로 404 또는 422를 반환해야 합니다.
현재 코드 경로
댓글 작성자 또는 관리 권한 조건만 충족하면 URL의 postId와 무관하게 대상 댓글을 수정·삭제할 수 있습니다.
완료 조건
- 댓글 조회 Repository 또는 Service가
board_id + post_id + comment_id로 대상을 제한해야 합니다.
- 다른 게시글 URL을 사용한 수정·삭제 요청은 실패해야 합니다.
- 사용자 및 관리자 API 모두 교차 게시글 경로 테스트를 추가해야 합니다.
검증 상태 및 제한
다음 사항은 소스 코드에서 확인했습니다.
- 기본 대댓글 깊이 10과 서비스의 숫자 5 하드코딩 불일치
- 5단계 이후 검증식과 저장식이 반복되는 결정적 실행 흐름
- 부모 댓글 검증의
post_id 조건 누락
- 댓글 수정·삭제에서 URL의
postId 미사용
- 관련 경계 테스트 누락
현재 로컬 환경에는 .env.testing 파일이 없어 Laravel HTTP 자동 테스트는 실행하지 못했습니다. 실제 수정 시 위 재현 시나리오를 Feature 테스트로 추가해야 합니다.
권장 처리 순서
- 대댓글
depth 숫자 5 하드코딩 제거 및 경계 테스트 추가
- 부모 댓글의 동일 게시글 소속 검증 추가
- 댓글 수정·삭제의
postId 스코프 검증 추가
- 잘못된 부모 관계로 이미 저장된 데이터 탐지 쿼리 및 정리 절차 제공
sirsoft-board 댓글·대댓글 깊이 및 무결성 버그 보고
요약
sirsoft-board의 댓글·대댓글 처리에서 다음 세 가지 문제가 확인됩니다.max_comment_depth는 최대 10이지만 저장depth가 5로 고정됨parent_id가 같은 게시글의 댓글인지 검증하지 않음postId를 사용하지 않음가장 우선도가 높은 문제는 첫 번째입니다. 기본
max_comment_depth가 10이므로 별도 설정을 변경하지 않은 게시판도 영향을 받습니다.확인 환경
7.0.4sirsoft-board 1.2.0348efa2e98df22b00d30d3a4e267d0305f2017582026-07-17관련 원본 파일에 로컬 변경이 없는 상태에서 분석했습니다.
이슈 1. max_comment_depth 6~10에서 depth가 5로 고정되고 제한이 무력화됨
제안 제목
문제 정의
게시판 설정과 DB 스키마는 대댓글 깊이를 최대 10까지 지원하지만, 댓글 생성 서비스는 계산된
depth를 숫자 5로 강제 제한합니다.설정 기본값:
근거:
modules/_bundled/sirsoft-board/config/settings/defaults.json:18설정 허용 범위:
근거:
modules/_bundled/sirsoft-board/config/board.php:150-152DB 기본값:
근거:
modules/_bundled/sirsoft-board/database/migrations/2026_04_01_000002_create_boards_table.php:46실제 댓글 생성 코드:
근거:
modules/_bundled/sirsoft-board/src/Services/CommentService.php:252-257깊이 검증 코드:
근거:
modules/_bundled/sirsoft-board/src/Rules/CommentValidationRule.php:113-116재현 절차
max_comment_depth를 10으로 설정합니다.parent_id로 지정합니다.depth를 확인합니다.사용자 댓글 생성 API:
라우트 근거:
modules/_bundled/sirsoft-board/src/routes/api.php:479-488예상 결과
201 Created422 Unprocessable Entity실제 결과
현재 코드 수식을 그대로 적용하면 다음과 같이 처리됩니다.
max_comment_depth=10일 때 부모depth가 5이면 검증식은 항상5 + 1 > 10이 되어 실패하지 않습니다. 생성 서비스는 다시min(6, 5)를 저장하므로 이후 부모 댓글도 계속depth=5입니다.사용자 화면 영향
사용자 템플릿은 API의
depth만큼 들여쓰기하고, 답글 버튼도depth < max_comment_depth조건으로 표시합니다.근거:
templates/_bundled/sirsoft-basic/layouts/partials/board/show/_comment_item.json:11,:121따라서 6단계 이후 댓글은 계속 5단계로 표시되면서 답글 버튼은 계속 노출됩니다.
성능 영향
댓글 목록은 부모·자식 정렬과 자손 댓글 수 계산을 재귀적으로 수행합니다.
근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:160-195modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:206-235깊이 제한이 무력화된 긴 단일 체인에서는 재귀 깊이가 증가합니다. 또한 모든 댓글에서 다시 전체 자손을 계산하므로 긴 체인일수록 CPU 비용이 커질 수 있습니다.
테스트 누락
현재 경계 테스트는
max_comment_depth=2만 사용합니다. 서비스에 하드코딩된 5를 넘지 않기 때문에 이 문제를 검출하지 못합니다.근거:
modules/_bundled/sirsoft-board/tests/Feature/User/CommentCrudScenarioTest.php:189-205수정 방향
생성 서비스의 숫자 5 하드코딩을 제거하고, 게시판별 최대 깊이 초과는 기존
CommentValidationRule에서 거부하는 방식이 적절합니다. 저장 단계에서도 방어적으로 계산된 깊이가 게시판 설정을 초과하지 않는지 확인할 수 있습니다.완료 조건
max_comment_depth=10에서depth=1~10이 정확히 저장됩니다.depth=10댓글에 답글을 작성하면422를 반환합니다.max_comment_depth=0,1,5,6,10경계 테스트가 추가됩니다.depth와 실제parent_id체인의 깊이가 일치합니다.이슈 2. 다른 게시글 댓글을 parent_id로 지정할 수 있음
제안 제목
문제 정의
대댓글 생성 요청은 URL의
postId를post_id로 사용하지만,parent_id검증은 부모 댓글의board_id만 확인합니다. 부모 댓글의post_id가 현재 게시글과 같은지 확인하지 않습니다.요청 검증:
근거:
modules/_bundled/sirsoft-board/src/Http/Requests/StoreCommentRequest.php:75-80부모 댓글 조회:
근거:
modules/_bundled/sirsoft-board/src/Rules/CommentValidationRule.php:88-93Repository 조회도 게시판과 댓글 ID만 사용합니다.
근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:259-267재현 절차
parent_id로 전달합니다.예상 결과
부모 댓글이 현재 게시글 소속이 아니므로
422 Unprocessable Entity를 반환해야 합니다.현재 코드 경로
같은 게시판의 유효한 댓글이므로 부모 댓글 검증을 통과합니다. 생성되는 행은
post_id=게시글 B,parent_id=게시글 A의 댓글조합을 가질 수 있습니다.댓글 목록 조회는 현재 게시글의
post_id로만 댓글을 가져오고parent_id=null인 댓글부터 트리를 구성합니다.근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:45-48modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:160-175따라서 게시글 B에 저장된 자식 댓글은 부모가 조회 결과에 없어 화면에서 누락될 수 있습니다.
추가 영향
replies_count가 다른 게시글의 대댓글로 증가할 수 있습니다.parent_id외래키가 없으므로 애플리케이션 검증 누락이 그대로 데이터 무결성 훼손으로 이어집니다.근거:
modules/_bundled/sirsoft-board/src/Listeners/CommentReplySyncListener.php:47-54modules/_bundled/sirsoft-board/src/Listeners/BoardNotificationDataListener.php:181-206modules/_bundled/sirsoft-board/database/migrations/2026_04_01_000005_create_board_comments_table.php:28테스트 누락
현재 테스트는 존재하지 않는
parent_id만 확인합니다. 같은 게시판의 다른 게시글에 존재하는 댓글 ID는 테스트하지 않습니다.근거:
modules/_bundled/sirsoft-board/tests/Feature/User/CommentCrudScenarioTest.php:208-221완료 조건
board_id와post_id가 현재 요청 대상과 모두 일치해야 합니다.parent_id로 전달하면422를 반환해야 합니다.이슈 3. 댓글 수정·삭제에서 URL postId를 검증하지 않음
제안 제목
문제 정의
사용자와 관리자 댓글 수정·삭제 컨트롤러는
$postId를 인자로 받지만 댓글 조회 및 수정·삭제 과정에서 사용하지 않습니다.사용자 API 근거:
modules/_bundled/sirsoft-board/src/Http/Controllers/User/CommentController.php:119-149modules/_bundled/sirsoft-board/src/Http/Controllers/User/CommentController.php:165-195관리자 API 근거:
modules/_bundled/sirsoft-board/src/Http/Controllers/Admin/CommentController.php:121-144modules/_bundled/sirsoft-board/src/Http/Controllers/Admin/CommentController.php:160-179댓글 Repository는
board_id + commentId로만 댓글을 찾습니다.근거:
modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php:279-287재현 절차
예상 결과
댓글이 URL의 게시글에 속하지 않으므로
404또는422를 반환해야 합니다.현재 코드 경로
댓글 작성자 또는 관리 권한 조건만 충족하면 URL의
postId와 무관하게 대상 댓글을 수정·삭제할 수 있습니다.완료 조건
board_id + post_id + comment_id로 대상을 제한해야 합니다.검증 상태 및 제한
다음 사항은 소스 코드에서 확인했습니다.
post_id조건 누락postId미사용현재 로컬 환경에는
.env.testing파일이 없어 Laravel HTTP 자동 테스트는 실행하지 못했습니다. 실제 수정 시 위 재현 시나리오를 Feature 테스트로 추가해야 합니다.권장 처리 순서
depth숫자 5 하드코딩 제거 및 경계 테스트 추가postId스코프 검증 추가