[ 게시글 상세보기 기능 추가 ]
중프 때 만든 게시글 상세보기는
중프 설계 기준으로는 정상적으로 돌아갔다.
하지만 커뮤니티 운영 관점에서
꼭 필요한 요소들이 빠져 있었다.
신고 기능이 없어서
문제 게시글을 수집할 방법이 없었고,
댓글 작성자 정보도
닉네임 텍스트 정도만 보여줘서
커뮤니티 느낌이 약했다.
또 작성/수정일이 명확하지 않아서
‘수정된 글인지’가 UX로 전달되지 않았다.
이번 작업에서는 이 3가지를 한 번에
보강하면서, 페이지 구조도 함께 정리했다.
- 신고 버튼 + 신고 모달
- 댓글 작성자 프로필(사진/꾸밈) 노출
- 게시글/댓글 작성일·수정일 표시 규칙 적용
- 그리고 유지보수를 위해 JSP / CSS / JS 분리
1. 구조 분리
중프 버전은 JSP 안에 CSS/JS가 섞여 있어서
수정할 때마다 찾기 힘들고 충돌도 자주 났다.
최종 버전에서는 전용 파일 3개로 분리했다.
전 → 후
전(중프: 한 파일에 몰아넣음)
<style>
/* board detail css ... */
</style>
<script>
/* board detail js ... */
</script>
후(최종: 파일 분리)
<link rel='stylesheet' href='${ctx}/css/boarddetail.css' />
<script src='${ctx}/js/boarddetail.js'></script>
2. ctx 스코프
헤더를 jsp:include로 붙이는 구조에서는
include 내부에서도 ctx를 쓰는 경우가 많다.
그래서 ctx는 request scope로 올려서
include에서도 안전하게 참조하도록 바꿨다.
전 → 후
전
<c:set var='ctx' value='${pageContext.request.contextPath}' />
후
<c:set var='ctx' value='${pageContext.request.contextPath}' scope='request' />
3. 신고 버튼
중프에는 신고 버튼 자체가 없었다.
최종에서는 '로그인+본인글 아님+아직 신고 안 함'일 때만
버튼이 렌더링되도록 했다.
전 → 후
전
(없음)
후(JSTL로 노출 조건 구성)
<c:set var='isReportedFlag'
value='${isReported == true or isReported == 1 or isReported == "1"}' />
<c:set var='canReportPost'
value='${isLogin and (sessionMemberId ne boardData.memberId) and (not isReportedFlag)}' />
<c:if test='${canReportPost}'>
<button id='btnReport' type='button' class='bd-btn danger'>신고</button>
</c:if>
신고는 모달로 받고 접수 성공 시
버튼을 즉시 숨겨 UX를 마무리했다.
// 성공 시
if ($btnReport) $btnReport.style.display = 'none';
4. 작성일·수정일
중프는 작성일/수정일을
제대로 분리해서 보여주지 않았다.
최종에서는 수정된 경우에만 수정일 칩을
추가로 노출하는 규칙을 넣었다.
전 → 후
전(작성자/조회수 정도만)
작성자: ${boardData.writerNickname}
조회수: ${boardData.boardViews}
후(작성일은 항상, 수정일은 수정된 경우만)
<c:set var='createdAt' value='${boardData.boardCreatedAt}' />
<c:set var='updatedAt' value='${boardData.boardUpdatedAt}' />
<c:set var='boardIsEdited'
value='${boardData.isEdited == 1 or boardData.isEdited == true or boardData.isEdited == "1"
or (not empty updatedAt and updatedAt ne createdAt)}' />
<span class='meta-chip'>작성일 ${createdAt}</span>
<c:if test='${boardIsEdited and not empty updatedAt}'>
<span class='meta-chip'>수정일 ${updatedAt}</span>
</c:if>
5. CKEditor 본문 안정화
중프는 본문을 pre-wrap으로 고정해서,
CKEditor HTML(문단/리스트/표)이 들어오면
레이아웃이 깨질 수 있었다.
최종은 기본은 normal로 살리고,
code/pre만 스크롤을 허용하는 방식으로 정리했다.
전 → 후
전
.bd-content-box .bd-content {
white-space: pre-wrap;
}
후
.board-detail-page .bd-content {
white-space: normal;
overflow-wrap: anywhere;
}
.board-detail-page .bd-content pre {
white-space: pre;
overflow: auto;
}
표/이미지도 깨짐 방지 처리를 추가했다.
.board-detail-page .bd-content table {
display: block;
overflow-x: auto;
width: 100%;
}
.board-detail-page .bd-content img,
.board-detail-page .bd-content iframe {
max-width: 100% !important;
}
6. 댓글 렌더 방식
중프는 댓글을 서버 렌더링(c:forEach)으로 찍었다.
최종은 정렬/갱신을 위해
댓글을 비동기로 다시 렌더링한다.
전 → 후
전(서버 렌더링)
<c:forEach var='r' items='${replyList}'>
<div class='reply-item'>
<span class='reply-nick'>${r.writerNickname}</span>
<div class='reply-content'>${r.replyContent}</div>
</div>
</c:forEach>
후(비동기 렌더링 자리만 남김)
<div id='replyList' class='reply-list'>
<div class='reply-empty' id='replyEmpty' style='display:none;'>
등록된 댓글이 없습니다.
</div>
</div>
그리고 JS에서 목록을 받아서
renderReplyItem()로 DOM을 구성한다.
function loadReplies(condition) {
// GET 호출 → list 받아서 → renderReplyItem 반복
}
7. 댓글 프로필
중프 댓글은 닉네임 텍스트만 보여줬다.
최종에서는 댓글 렌더링 시
프로필 이미지/색/꾸밈 클래스까지 반영한다.
전 → 후
전
<span class='reply-nick'>${r.writerNickname}</span>
후(JS 렌더에서 아바타 + 꾸밈 클래스 포함)
return (
"<div class='reply-item'>" +
"<div class='reply-top'>" +
"<div style='display:flex; gap:10px;'>" +
avatarHtml +
"<div class='reply-writer " + decoClass + "'>" + nickname + "</div>" +
"</div>" +
"</div>" +
"</div>"
);
8. danger 버튼 톤
신고 버튼은 ‘경고 느낌’이 확실해야 해서
danger만 컬러/그라데이션을 강화했다.
또 disabled 공통 처리 때문에
danger가 평범해지는 걸 막기 위해
danger disabled hover는 예외 처리했다.
전 → 후
전(기본 danger)
.btn-danger2 {
background: linear-gradient(...);
}
후(danger 강화 + disabled hover 예외)
.board-detail-page .bd-btn.danger {
background: linear-gradient(135deg, rgba(255, 70, 95, 0.55), rgba(0, 0, 0, 0.18));
border-color: rgba(255, 90, 120, 0.55);
}
.board-detail-page .bd-btn:disabled:hover {
background: var(--btn-bg);
border-color: var(--btn-border);
}
.board-detail-page .bd-btn.danger:disabled:hover {
background: linear-gradient(135deg, rgba(255, 70, 95, 0.55), rgba(0, 0, 0, 0.18));
border-color: rgba(255, 90, 120, 0.55);
}
마무리
정리하면 이번 보드 디테일 개선은
기능 3종 추가에서 끝난 게 아니라,
그 기능들이 자연스럽게 유지보수되도록
구조까지 함께 정리한 작업이었다.
- 신고 UX를 추가하면서
권한/노출 조건을 JSP에서 확정 - 댓글은 비동기 렌더로 전환해
정렬/갱신 구조를 단순화 - 프로필/꾸밈/수정일 같은
운영 요소를 UI로 드러나게 개선 - CSS/JS 분리 + 스코프 적용으로
페이지 단위 유지보수 가능
변경 포인트 요약 체크리스트
[ 구조 ]
> JSP / CSS / JS 분리 CSS 스코프를
body.board-detail-page로 제한
> ctx를 request scope로 올려
include(header)에서도 사용 가능하게 정리
[ 게시글 영역 ]
> 작성일/수정일 표시 규칙 적용
(수정된 경우만 수정일 노출)
> 본문은 CKEditor HTML 렌더 기준으로
스타일 리셋(white-space normal)
> 표/코드/이미지/iframe
오버플로 대응(모바일 포함)
[ 신고 기능 ]
> 신고 버튼 노출 조건 추가
(로그인 + 본인글 아님 + 미신고)
> 신고 모달 UI 추가
+ 접수 성공 시 버튼 즉시 숨김 처리
> danger 버튼 톤 강화
(hover/active/focus/disabled 예외 포함)
[ 댓글 영역 ]
> 댓글 목록을 비동기
렌더링 구조로 전환(정렬/갱신용)
> 댓글 작성자 프로필(사진/색/꾸밈 클래스) 노출
> 댓글 작성일/수정일 표기
+ 수정 여부 판단 로직 적용
> 댓글 권한 규칙 적용
(작성자 수정, 작성자/관리자 삭제)
[ 좋아요 ]
> 좋아요 토글 UI/카운트 동기화
> 좋아요 누른 사람 목록 모달/알림 처리
'개주 훈련일지 > 🏋️ 전집중 호흡 훈련' 카테고리의 다른 글
| 프론트 경로 통일 리팩토링: ctx 기준 링크와 리소스 안정화 (0) | 2026.02.19 |
|---|---|
| 네이버 스마트스토어 Oracle → MySQL 무중단 마이그레이션 사례 리뷰 (0) | 2026.02.18 |
| 애니 리스트에 연도/분기 필터 추가하기 (비동기: 정렬·필터·페이징 / 동기: 검색) (0) | 2026.02.15 |
| 관리자 대시보드 제작기: 더미 데이터 기반 화면을 서버 연동 구조로 전환하기 (0) | 2026.02.14 |
| 형상관리 개선 기록: viewdevelop 브랜치를 만든 이유 (스프링 부트 기준) (2) | 2026.02.13 |