테스트 사이트 - 개발 중인 베타 버전입니다

파싱도와주세요ㅠ 채택완료

fdud3889 9년 전 조회 6,083

스누피 클래스를 사용해서 파싱에는 성공했습니다ㅠ

파싱한 내용을 한 셀에 나오게 엑셀로 저장하려고하는데 내용은 나오지만

태그가 먹은건지 소스에 적용된 HTML 태그가 모두 적용되어서 폰트색상이 변경되고, 개행도되서 글내용부분 셀만 몇십줄로나옵니다..

HTML 태그채로(

그누보드

) 저장하고싶어서 strip_tags도 사용안했습니다.ㅠㅜ 

도저히 해결이안되서 질문드립니다ㅠ 

 

아래는 작업한 소스입니다ㅠ 

염치없지만 능력자님들 도와주세요ㅠㅜ

 

</p><p><?php</p><p> </p><p>header( "Content-type: application/vnd.ms-excel; charset=utf-8" );</p><p>header( "Expires: 0" );</p><p>header( "Cache-Control: must-revalidate, post-check=0,pre-check=0" );</p><p>header( "Pragma: public" );</p><p>header( "Content-Disposition: attachment; filename=test.xls" );</p><p> </p><p>require($_SERVER['DOCUMENT_ROOT'].'/snoopy/Snoopy.class.php');</p><p> </p><p>$snoopy = new Snoopy;</p><p> </p><p>$f_url="xxx";</p><p>$snoopy->fetch($f_url);</p><p>$result = $snoopy->results; </p><p> </p><p>preg_match('/<div class="info_detail">(.*?)<\/div>/is', $result, $el4);</p><p>$data['content'] = $el4[1];</p><p> </p><p>echo "<table>"; </p><p>echo "<tr><td>".$data['content']."</td></tr>"; </p><p>echo "</table>"; </p><p>?></p><p>

댓글을 작성하려면 로그인이 필요합니다.

답변 5개

ahckdhls
9년 전
자문자답

먼저 그림자밟기님 답변감사합니다!

해당문제는 phpexcel 클래스를 통해서 저장하니 해결됬습니다~
로그인 후 평가할 수 있습니다

댓글을 작성하려면 로그인이 필요합니다.

그림자밟기

아.

echo '="'.addslashes($data['content']).'"'; 

요렇게.

로그인 후 평가할 수 있습니다

답변에 대한 댓글 1개

f
fdud3889
9년 전
답변감사합니다!
하지만 이렇게해도 엑셀에서 제일위에 =" 추가되는거 말고는 다른게없네요ㅠㅠ
(html태그+내용)을 뽑아서 다시 디비에 올릴려고 합니다.
addslashes를 사용하면 나중에 올릴때 문제가없을까요?

댓글을 작성하려면 로그인이 필요합니다.

ahckdhls
9년 전

답변감사합니다. 

addslashes로 해봤는데 줄마다 개행이되서 똑같이 출력되네요ㅠㅠㅠ 

제가 원하는건

태그를 썻을때처럼 HTML 코드도 텍스트 그대로 출력되게하고싶습니다.</p><p>그래서 찾은게 <span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);">htmlspecialchars 함수인데 어떻게 적용해야하는지.. 3시간동안 여기저기 </span><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);">htmlspecialchars함수를 집어넣어봤는데 적용이안되네요.. 혹시 방법이 없을까요??</span></p><p><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);"><img src="//sir.kr/data/editor/1608/9d78cbae2378a598063102d2027ffb7b_1471231870_5318.jpeg" title="9d78cbae2378a598063102d2027ffb7b_1471231870_5318.jpeg"></span></p><p><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);"><img src="//sir.kr/data/editor/1608/9d78cbae2378a598063102d2027ffb7b_1471232095_1093.PNG" title="9d78cbae2378a598063102d2027ffb7b_1471232095_1093.PNG"><br style="clear:both;"> </span></p><p><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);">파싱한 데이터를 위사진처럼 소스코드 그대로 엑셀에 하나의 셀에 저장하고싶습니다. </span><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);"> </span></p><p><span style="color: rgb(140, 138, 140); font-size: 12px; line-height: 18px; text-align: justify; background-color: rgb(255, 255, 255);"> </span></p> </div> <!-- 좋아요/싫어요 및 액션 버튼 --> <div class="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-700"> <div class="flex items-center space-x-4"> <div class="text-sm text-gray-500 dark:text-gray-400"> 로그인 후 평가할 수 있습니다 </div> </div> </div> </div> <!-- 댓글 섹션 (답변 카드 안에 포함) --> <div class="mx-6 mb-6"> <div class="relative"> <!-- 연결선 표시 --> <div class="absolute -top-3 left-8 w-px h-6 bg-gradient-to-b from-gray-300 to-transparent dark:from-gray-600"></div> <div class="px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700"> <!-- 댓글 목록 --> <!-- 디버깅: 답변 137329의 전체 댓글 구조 --> <!-- 댓글 작성 폼 --> <div class="text-center py-3"> <p class="text-sm text-gray-500 dark:text-gray-400"> 댓글을 작성하려면 <a href="/login" class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">로그인</a>이 필요합니다. </p> </div> </div> </div> </div> </div> <div id="answer_137327" class="bg-white dark:bg-gray-800 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden border border-gray-200 dark:border-gray-700"> <div class="p-6"> <!-- 답변 작성자 정보 --> <div class="flex items-center justify-between mb-5"> <div class="flex items-center space-x-4"> <div class="relative inline-block" style="width: 48px; height: 48px;"> <!-- 프로필 이미지 사용 (업로드/소셜/레거시) --> <img src="https://ssl.pstatic.net/static/pwe/address/img_profile.png" alt="그림자밟기" class="w-full h-full rounded-full object-cover border-4 border-white dark:border-slate-600 shadow-lg" onerror="this.style.display='none'; this.nextElementSibling.style.display='block';"> <!-- 이미지 로드 실패 시 SVG 아바타 표시 --> <svg width="48" height="48" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" class="rounded-full" style="display: none;"> <defs> <linearGradient id="grad-fallback-avatar-6915df8a4cfc42.96160386" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" style="stop-color:hsl(137, 77%, 57%);stop-opacity:1" /> <stop offset="100%" style="stop-color:hsl(167, 77%, 52%);stop-opacity:1" /> </linearGradient> </defs> <circle cx="50" cy="50" r="50" fill="url(#grad-fallback-avatar-6915df8a4cfc42.96160386)" /> <text x="50" y="50" font-size="45" fill="white" text-anchor="middle" dy=".35em" font-weight="bold" font-family="sans-serif"> 그 </text> </svg> </div> <div> <div class="flex items-center space-x-2"> <a href="https://new-sir.gnuboard.net/profile/34949888-b07e-45a4-993c-3d3d919c07b1" class="font-semibold text-gray-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> 그림자밟기 </a> </div> <div class="text-sm text-gray-500 dark:text-gray-400">9년 전</div> </div> </div> <!-- 답변 액션 버튼들 --> <div class="flex items-center space-x-2"> </div> </div> <!-- 답변 내용 --> <div class="prose prose-lg max-w-none dark:prose-invert text-gray-800 dark:text-gray-200 leading-relaxed mb-6 qa-content"> <p>echo "<tr><td>".$data['content']."</td></tr>"; </p><p> </p><p>=></p><p> </p><p>echo '<tr><td>"'.addslashes($data['content']).'"</td></tr>'; </p><p> </p><p>요렇게 한 번 해보세요.</p> </div> <!-- 좋아요/싫어요 및 액션 버튼 --> <div class="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-700"> <div class="flex items-center space-x-4"> <div class="text-sm text-gray-500 dark:text-gray-400"> 로그인 후 평가할 수 있습니다 </div> </div> </div> </div> <!-- 댓글 섹션 (답변 카드 안에 포함) --> <div class="mx-6 mb-6"> <div class="relative"> <!-- 연결선 표시 --> <div class="absolute -top-3 left-8 w-px h-6 bg-gradient-to-b from-gray-300 to-transparent dark:from-gray-600"></div> <div class="px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700"> <!-- 댓글 목록 --> <!-- 디버깅: 답변 137327의 전체 댓글 구조 --> <!-- 댓글 수: 1, 대댓글 그룹 수: 0 --> <div class="mb-4"> <div class="flex items-center gap-2 mb-4"> <svg class="w-4 h-4 text-gray-600 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-4 4z"></path> </svg> <h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">답변에 대한 댓글 1개</h4> </div> <div class="space-y-3"> <div id="c_137330" class="relative "> <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-200"> <div class="p-4"> <div class="flex items-start justify-between mb-3"> <div class="flex items-center gap-3"> <div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-medium"> f </div> <div> <div class="font-medium text-sm text-gray-900 dark:text-white"> fdud3889 </div> <div class="text-xs text-gray-500 dark:text-gray-400" title="2016-08-15 (월) 12:21:03"> 9년 전 </div> </div> </div> </div> <div class="text-sm text-gray-700 dark:text-gray-300 comment-content leading-relaxed"> 답변감사합니다. <br /> addslashes로 해봤는데 줄마다 개행이되서 똑같이 출력되네요ㅠㅠㅠ <br /> 제가 원하는건 &lt;XMP&gt; 태그를 썻을때처럼 HTML 코드도 텍스트 그대로 출력되게하고싶습니다.<br /> 그래서 찾은게 htmlspecialchars 함수인데 어떻게 적용해야하는지.. 3시간동안 여기저기 htmlspecialchars함수를 집어넣어봤는데 적용이안되네요.. 혹시 방법이 없을까요?? </div> <!-- 댓글 액션 버튼 --> <div class="flex items-center gap-2 mt-3 pt-2 border-t border-gray-100 dark:border-gray-700"> </div> <!-- 댓글 수정 폼 (숨김) --> <div class="comment-edit-form hidden mt-3"> <textarea class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white text-sm resize-none" rows="3" placeholder="댓글을 수정하세요...">답변감사합니다. addslashes로 해봤는데 줄마다 개행이되서 똑같이 출력되네요ㅠㅠㅠ 제가 원하는건 &lt;XMP&gt; 태그를 썻을때처럼 HTML 코드도 텍스트 그대로 출력되게하고싶습니다. 그래서 찾은게 htmlspecialchars 함수인데 어떻게 적용해야하는지.. 3시간동안 여기저기 htmlspecialchars함수를 집어넣어봤는데 적용이안되네요.. 혹시 방법이 없을까요??</textarea> <div class="flex justify-end gap-2 mt-2"> <button type="button" class="comment-save-btn px-3 py-1.5 bg-qa-btn-primary-bg-light hover:bg-qa-btn-primary-hover-light dark:bg-qa-btn-primary-bg-dark dark:hover:bg-qa-btn-primary-hover-dark text-white rounded-lg text-sm font-medium transition-colors" data-comment-id="137330">저장</button> <button type="button" class="comment-cancel-btn px-3 py-1.5 bg-gray-500 hover:bg-gray-600 text-white rounded-lg text-sm font-medium transition-colors">취소</button> </div> </div> <!-- 대댓글 작성 폼 (숨김) --> <div class="reply-form hidden mt-3 bg-gray-50 dark:bg-gray-700 rounded-lg p-3"> <div class="flex items-center gap-2 mb-2"> <svg class="w-3 h-3 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path> </svg> <span class="text-xs text-gray-600 dark:text-gray-300 reply-target"></span> </div> <form class="reply-form-element" data-parent-id="137327" data-comment-id="137330"> <input type="hidden" name="_token" value="xf0vYA2tobtVBegRoJ2gbwOeZxvfTYLWVP69qrgx" autocomplete="off"> <textarea name="wr_content" placeholder="답글을 작성하세요..." rows="2" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:text-white text-sm resize-none" required></textarea> <div class="flex justify-end gap-2 mt-2"> <button type="button" class="reply-cancel-btn px-3 py-1.5 bg-gray-500 hover:bg-gray-600 text-white rounded text-sm font-medium transition-colors">취소</button> <button type="submit" class="px-3 py-1.5 bg-qa-btn-primary-bg-light hover:bg-qa-btn-primary-hover-light dark:bg-qa-btn-primary-bg-dark dark:hover:bg-qa-btn-primary-hover-dark text-white rounded text-sm font-medium transition-colors">답글 등록</button> </div> </form> </div> </div> </div> </div> <!-- 대댓글 표시 --> <!-- 디버깅: 댓글 1, 답변ID 137327, 대댓글 있음: 아니오 --> </div> </div> <!-- 댓글 작성 폼 --> <div class="text-center py-3"> <p class="text-sm text-gray-500 dark:text-gray-400"> 댓글을 작성하려면 <a href="/login" class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">로그인</a>이 필요합니다. </p> </div> </div> </div> </div> </div> <div id="answer_137321" class="bg-white dark:bg-gray-800 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden border border-gray-200 dark:border-gray-700"> <div class="p-6"> <!-- 답변 작성자 정보 --> <div class="flex items-center justify-between mb-5"> <div class="flex items-center space-x-4"> <div class="relative inline-block" style="width: 48px; height: 48px;"> <!-- SVG 아바타 사용 (기본값) --> <svg width="48" height="48" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" class="rounded-full"> <!-- 배경 원 (그라데이션) --> <defs> <linearGradient id="grad-avatar-6915df8a4d3ab7.27252659" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" style="stop-color:hsl(278, 68%, 52%);stop-opacity:1" /> <stop offset="100%" style="stop-color:hsl(308, 68%, 47%);stop-opacity:1" /> </linearGradient> </defs> <circle cx="50" cy="50" r="50" fill="url(#grad-avatar-6915df8a4d3ab7.27252659)" /> <!-- 닉네임 첫 글자 --> <text x="50" y="50" font-size="45" fill="white" text-anchor="middle" dy=".35em" font-weight="bold" font-family="sans-serif"> 알 </text> </svg> </div> <div> <div class="flex items-center space-x-2"> <a href="https://new-sir.gnuboard.net/profile/cb363807-8126-40b8-a982-e155a9edb574" class="font-semibold text-gray-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-400 transition-colors"> 알미z </a> </div> <div class="text-sm text-gray-500 dark:text-gray-400">9년 전</div> </div> </div> <!-- 답변 액션 버튼들 --> <div class="flex items-center space-x-2"> </div> </div> <!-- 답변 내용 --> <div class="prose prose-lg max-w-none dark:prose-invert text-gray-800 dark:text-gray-200 leading-relaxed mb-6 qa-content"> <p> 문제점이 무엇인지 안나와있네여</p><p>어떻게 하고싶다는 의도신지 </p> </div> <!-- 좋아요/싫어요 및 액션 버튼 --> <div class="flex items-center justify-between pt-4 border-t border-gray-200 dark:border-gray-700"> <div class="flex items-center space-x-4"> <div class="text-sm text-gray-500 dark:text-gray-400"> 로그인 후 평가할 수 있습니다 </div> </div> </div> </div> <!-- 댓글 섹션 (답변 카드 안에 포함) --> <div class="mx-6 mb-6"> <div class="relative"> <!-- 연결선 표시 --> <div class="absolute -top-3 left-8 w-px h-6 bg-gradient-to-b from-gray-300 to-transparent dark:from-gray-600"></div> <div class="px-6 py-4 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700"> <!-- 댓글 목록 --> <!-- 디버깅: 답변 137321의 전체 댓글 구조 --> <!-- 댓글 수: 1, 대댓글 그룹 수: 0 --> <div class="mb-4"> <div class="flex items-center gap-2 mb-4"> <svg class="w-4 h-4 text-gray-600 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-4 4z"></path> </svg> <h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">답변에 대한 댓글 1개</h4> </div> <div class="space-y-3"> <div id="c_137331" class="relative "> <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-200"> <div class="p-4"> <div class="flex items-start justify-between mb-3"> <div class="flex items-center gap-3"> <div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white text-sm font-medium"> f </div> <div> <div class="font-medium text-sm text-gray-900 dark:text-white"> fdud3889 </div> <div class="text-xs text-gray-500 dark:text-gray-400" title="2016-08-15 (월) 12:21:35"> 9년 전 </div> </div> </div> </div> <div class="text-sm text-gray-700 dark:text-gray-300 comment-content leading-relaxed"> 답변감사합니다. <br /> 제가 원하는건 &lt;XMP&gt; 태그를 썻을때처럼 HTML 코드도 텍스트 그대로 출력되게하고싶습니다.<br /> 그래서 찾은게 htmlspecialchars 함수인데 어떻게 적용해야하는지.. 3시간동안 여기저기 htmlspecialchars함수를 집어넣어봤는데 적용이안되네요.. 혹시 방법이 없을까요?? </div> <!-- 댓글 액션 버튼 --> <div class="flex items-center gap-2 mt-3 pt-2 border-t border-gray-100 dark:border-gray-700"> </div> <!-- 댓글 수정 폼 (숨김) --> <div class="comment-edit-form hidden mt-3"> <textarea class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white text-sm resize-none" rows="3" placeholder="댓글을 수정하세요...">답변감사합니다. 제가 원하는건 &lt;XMP&gt; 태그를 썻을때처럼 HTML 코드도 텍스트 그대로 출력되게하고싶습니다. 그래서 찾은게 htmlspecialchars 함수인데 어떻게 적용해야하는지.. 3시간동안 여기저기 htmlspecialchars함수를 집어넣어봤는데 적용이안되네요.. 혹시 방법이 없을까요??</textarea> <div class="flex justify-end gap-2 mt-2"> <button type="button" class="comment-save-btn px-3 py-1.5 bg-qa-btn-primary-bg-light hover:bg-qa-btn-primary-hover-light dark:bg-qa-btn-primary-bg-dark dark:hover:bg-qa-btn-primary-hover-dark text-white rounded-lg text-sm font-medium transition-colors" data-comment-id="137331">저장</button> <button type="button" class="comment-cancel-btn px-3 py-1.5 bg-gray-500 hover:bg-gray-600 text-white rounded-lg text-sm font-medium transition-colors">취소</button> </div> </div> <!-- 대댓글 작성 폼 (숨김) --> <div class="reply-form hidden mt-3 bg-gray-50 dark:bg-gray-700 rounded-lg p-3"> <div class="flex items-center gap-2 mb-2"> <svg class="w-3 h-3 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"></path> </svg> <span class="text-xs text-gray-600 dark:text-gray-300 reply-target"></span> </div> <form class="reply-form-element" data-parent-id="137321" data-comment-id="137331"> <input type="hidden" name="_token" value="xf0vYA2tobtVBegRoJ2gbwOeZxvfTYLWVP69qrgx" autocomplete="off"> <textarea name="wr_content" placeholder="답글을 작성하세요..." rows="2" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-600 dark:text-white text-sm resize-none" required></textarea> <div class="flex justify-end gap-2 mt-2"> <button type="button" class="reply-cancel-btn px-3 py-1.5 bg-gray-500 hover:bg-gray-600 text-white rounded text-sm font-medium transition-colors">취소</button> <button type="submit" class="px-3 py-1.5 bg-qa-btn-primary-bg-light hover:bg-qa-btn-primary-hover-light dark:bg-qa-btn-primary-bg-dark dark:hover:bg-qa-btn-primary-hover-dark text-white rounded text-sm font-medium transition-colors">답글 등록</button> </div> </form> </div> </div> </div> </div> <!-- 대댓글 표시 --> <!-- 디버깅: 댓글 1, 답변ID 137321, 대댓글 있음: 아니오 --> </div> </div> <!-- 댓글 작성 폼 --> <div class="text-center py-3"> <p class="text-sm text-gray-500 dark:text-gray-400"> 댓글을 작성하려면 <a href="/login" class="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300">로그인</a>이 필요합니다. </p> </div> </div> </div> </div> </div> </div> <!-- 답변 페이징 --> </div> <!-- Error Messages --> <!-- Answer Form --> <div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-6 text-center"> <p class="text-gray-600 dark:text-gray-400 mb-4">답변을 작성하려면 로그인이 필요합니다.</p> <a href="https://new-sir.gnuboard.net/login?redirect=https%3A%2F%2Fnew-sir.gnuboard.net%2Fquestions%2F137317%3Fvpage%3D1" class="inline-block px-6 py-2 bg-qa-btn-primary-bg-light dark:bg-qa-btn-primary-bg-dark hover:bg-qa-btn-primary-hover-light dark:hover:bg-qa-btn-primary-hover-dark text-qa-btn-primary-text-light dark:text-qa-btn-primary-text-dark rounded-lg transition-colors"> 로그인 </a> </div> </div> <!-- Sidebar Component --> <!-- Sidebar --> <aside class="hidden lg:block w-80 flex-shrink-0 self-start"> <!-- 지금 질문하기 버튼 --> <div class="mb-4"> <a href="https://new-sir.gnuboard.net/questions/create" class="flex items-center justify-center gap-2 w-full px-4 py-3 bg-gradient-to-r from-qa-gradient-from-light to-qa-gradient-to-light dark:from-qa-gradient-from-dark dark:to-qa-primary-dark hover:from-qa-accent-light hover:to-qa-primary-light dark:hover:from-qa-primary-dark dark:hover:to-qa-gradient-to-dark text-qa-btn-primary-text-light dark:text-qa-btn-primary-text-dark font-medium rounded-lg shadow-md transition-all duration-200"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"></path> </svg> 지금 질문하기 </a> </div> <!-- 질문하는방법 --> <div class="bg-qa-card-bg-light dark:bg-qa-card-bg-dark rounded-lg shadow-sm p-6 mb-4"> <h3 class="text-lg font-bold text-qa-heading-light dark:text-qa-heading-dark mb-4"> <a href="https://new-sir.gnuboard.net/questions/howto" class="hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark transition-colors"> 질문하는방법 </a> </h3> <div class="space-y-3 text-sm"> <div class="flex justify-between items-center"> <a href="https://new-sir.gnuboard.net/questions" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">모든질문</a> <span class="font-semibold text-qa-heading-light dark:text-qa-heading-dark">129,285</span> </div> <div class="flex justify-between items-center"> <a href="https://new-sir.gnuboard.net/questions?unanswered=1" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">답변없는질문</a> <span class="font-semibold text-qa-heading-light dark:text-qa-heading-dark">8</span> </div> <div class="flex justify-between items-center"> <a href="https://new-sir.gnuboard.net/questions?unadopted=1" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">미채택된질문</a> <span class="font-semibold text-qa-heading-light dark:text-qa-heading-dark">18,376</span> </div> <div class="flex justify-between items-center"> <a href="https://new-sir.gnuboard.net/questions?adopted=1" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">채택된질문</a> <span class="font-semibold text-qa-heading-light dark:text-qa-heading-dark">101,681</span> </div> </div> <h4 class="text-sm font-semibold text-qa-heading-light dark:text-qa-heading-dark mt-6 mb-3">모든답변</h4> <div class="space-y-3 text-sm"> <div class="flex items-center gap-2"> <span class="inline-flex items-center justify-center w-6 h-6 bg-qa-icon-badge-bg-light dark:bg-qa-icon-badge-bg-dark text-xs font-medium text-qa-icon-badge-text-light dark:text-qa-icon-badge-text-dark rounded">5</span> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C5" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">그누보드5</a> <span class="ml-auto font-semibold text-qa-heading-light dark:text-qa-heading-dark">62,905</span> </div> <div class="flex justify-between items-center pl-8"> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%98%81%EC%B9%B4%ED%8A%B85" class="text-qa-link-light dark:text-qa-link-dark hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark">영카트5</a> <span class="font-semibold text-qa-heading-light dark:text-qa-heading-dark">17,535</span> </div> </div> <h4 class="text-sm font-semibold text-qa-heading-light dark:text-qa-heading-dark mt-6 mb-3"> <a href="https://new-sir.gnuboard.net/questions/tags" class="hover:text-qa-link-hover-light dark:hover:text-qa-link-hover-dark transition-colors"> 태그전체보기 </a> </h4> <div class="space-y-3 text-sm"> </div> </div> <!-- 관심태그 --> <div class="bg-qa-card-bg-light dark:bg-qa-card-bg-dark rounded-lg shadow-sm p-6 mb-4"> <h3 class="text-lg font-bold text-qa-heading-light dark:text-qa-heading-dark mb-4">관심태그</h3> <div class="flex flex-wrap gap-2"> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%98%81%EC%B9%B4%ED%8A%B85" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #영카트5 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C4" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드4 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%98%81%EC%B9%B4%ED%8A%B84" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #영카트4 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%A0%95%EA%B7%9C%EC%8B%9D" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #정규식 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%ED%85%8C%EB%A7%88" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #테마 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C5" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드5 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EB%84%A4%EC%9D%B4%EB%B2%84%ED%8E%98%EC%9D%B4" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #네이버페이 </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EC%BB%A4%EB%A8%B8%EC%8A%A4" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누커머스 </a> <a href="https://new-sir.gnuboard.net/questions?tag=php" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #php </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C6" class="inline-flex items-center gap-1 px-3 py-1.5 text-xs font-medium rounded-full transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드6 </a> </div> </div> <!-- 인기태그 --> <div class="bg-qa-card-bg-light dark:bg-qa-card-bg-dark rounded-lg shadow-sm p-6"> <h3 class="text-lg font-bold text-qa-heading-light dark:text-qa-heading-dark mb-4">인기태그</h3> <div class="flex flex-wrap gap-2"> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C5" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드5 <span class="text-[10px] opacity-75">(62,905)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%98%81%EC%B9%B4%ED%8A%B85" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #영카트5 <span class="text-[10px] opacity-75">(17,535)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=php" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #php <span class="text-[10px] opacity-75">(15,830)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C4" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드4 <span class="text-[10px] opacity-75">(10,649)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B2%8C%EC%8B%9C%ED%8C%90" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #게시판 <span class="text-[10px] opacity-75">(3,273)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=javascript" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #javascript <span class="text-[10px] opacity-75">(2,678)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EB%B3%B4%EB%93%9C" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누보드 <span class="text-[10px] opacity-75">(2,463)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=css" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #css <span class="text-[10px] opacity-75">(2,129)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=mysql" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #mysql <span class="text-[10px] opacity-75">(1,556)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%98%81%EC%B9%B4%ED%8A%B8" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #영카트 <span class="text-[10px] opacity-75">(1,295)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EB%AA%A8%EB%B0%94%EC%9D%BC" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #모바일 <span class="text-[10px] opacity-75">(1,293)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EA%B7%B8%EB%88%84%EC%BB%A4%EB%A8%B8%EC%8A%A4" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-interest-bg-light dark:bg-qa-tag-interest-bg-dark text-qa-tag-interest-text-light dark:text-qa-tag-interest-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #그누커머스 <span class="text-[10px] opacity-75">(1,178)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=html" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #html <span class="text-[10px] opacity-75">(1,133)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=jquery" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #jquery <span class="text-[10px] opacity-75">(1,027)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%B5%9C%EC%8B%A0%EA%B8%80" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #최신글 <span class="text-[10px] opacity-75">(970)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #자바스크립트 <span class="text-[10px] opacity-75">(953)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=DB" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #DB <span class="text-[10px] opacity-75">(850)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EB%A1%9C%EA%B7%B8%EC%9D%B8" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #로그인 <span class="text-[10px] opacity-75">(821)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%9D%B4%EB%AF%B8%EC%A7%80" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #이미지 <span class="text-[10px] opacity-75">(785)</span> </a> <a href="https://new-sir.gnuboard.net/questions?tag=%EC%97%AC%EB%B6%84%ED%95%84%EB%93%9C" class="inline-flex items-center gap-1 px-2.5 py-1 text-xs rounded transition-colors bg-qa-tag-bg-light dark:bg-qa-tag-bg-dark text-qa-tag-text-light dark:text-qa-tag-text-dark hover:bg-qa-tag-hover-light dark:hover:bg-qa-tag-hover-dark " title="태그 추가"> #여분필드 <span class="text-[10px] opacity-75">(784)</span> </a> </div> </div> </aside> </div> </div> <!-- Share Modal --> <div id="share-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden"> <div class="flex items-center justify-center min-h-screen p-4"> <div class="bg-white dark:bg-gray-800 rounded-lg max-w-md w-full p-6"> <div class="flex items-center justify-between mb-4"> <h3 class="text-lg font-medium text-gray-900 dark:text-white">공유하기</h3> <button onclick="closeShareModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> </svg> </button> </div> <div class="flex items-center gap-4"> <a href="#" id="facebook-share" target="_blank" class="flex items-center justify-center w-12 h-12 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"> <i class="fa fa-facebook"></i> </a> <a href="#" id="twitter-share" target="_blank" class="flex items-center justify-center w-12 h-12 bg-blue-400 text-white rounded-lg hover:bg-blue-500 transition-colors"> <i class="fa fa-twitter"></i> </a> <button onclick="copyToClipboard()" class="flex items-center justify-center w-12 h-12 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"> <i class="fa fa-link"></i> </button> </div> </div> </div> </div> <!-- 신고 모달 다이얼로그 --> <div id="sir_react_singod" style="display: none;"> <p>이 게시물을 신고 하시겠습니까?<br>신고사유를 선택해주세요.</p> <ul class="space-y-2"> <li> <input type="radio" name="rdo_reason" id="rdo_reason_ad" value="광고"> <label for="rdo_reason_ad" class="ml-2">광고성 게시물</label> </li> <li> <input type="radio" name="rdo_reason" id="rdo_reason_ob" value="음란"> <label for="rdo_reason_ob" class="ml-2">음란성 게시물</label> </li> <li> <input type="radio" name="rdo_reason" id="rdo_reason_ha" value="혐오"> <label for="rdo_reason_ha" class="ml-2">상대방 비방 및 혐오</label> </li> <li> <input type="radio" name="rdo_reason" id="rdo_reason_etc" value="기타"> <label for="rdo_reason_etc" class="ml-2">기타</label> <div class="mt-2"> <label for="sg_reason" class="block text-sm text-gray-600 dark:text-gray-400">기타 신고사유</label> <input type="text" name="sg_reason" id="sg_reason" class="mt-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white" placeholder="10글자 이상 입력해주세요"> </div> </li> </ul> </div> <script> document.addEventListener('DOMContentLoaded', function() { // 좋아요 기능 const voteGoodBtn = document.getElementById('vote-good-btn'); if (voteGoodBtn) { voteGoodBtn.addEventListener('click', function() { const wrId = this.dataset.wrId; const countSpan = this.querySelector('.vote-good-count'); const textSpan = this.querySelector('.vote-good-text'); const isVoted = textSpan.textContent === '좋아요됨'; if (this.disabled) return; this.disabled = true; this.classList.add('opacity-50'); fetch(`/questions/${wrId}/vote`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ type: 'good' }) }) .then(response => response.json()) .then(data => { if (data.success) { countSpan.textContent = data.good_count; if (data.user_vote === null) { // 취소된 경우 textSpan.textContent = '좋아요'; this.classList.remove('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300'); this.classList.add('bg-green-100', 'dark:bg-green-900', 'text-green-700', 'dark:text-green-300', 'hover:bg-green-200', 'dark:hover:bg-green-800'); } else { // 투표된 경우 textSpan.textContent = '좋아요됨'; this.classList.remove('bg-green-100', 'dark:bg-green-900', 'text-green-700', 'dark:text-green-300', 'hover:bg-green-200', 'dark:hover:bg-green-800'); this.classList.add('bg-blue-100', 'dark:bg-blue-900', 'text-blue-700', 'dark:text-blue-300'); } console.log('Success message:', data.message); showMessage(data.message, 'success'); } else { console.log('Error message:', data.error); showMessage(data.error || '좋아요에 실패했습니다.', 'error'); } this.disabled = false; this.classList.remove('opacity-50'); }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); this.disabled = false; this.classList.remove('opacity-50'); }); }); } // 싫어요 기능 const voteNogoodBtn = document.getElementById('vote-nogood-btn'); if (voteNogoodBtn) { voteNogoodBtn.addEventListener('click', function() { const wrId = this.dataset.wrId; const countSpan = this.querySelector('.vote-nogood-count'); const textSpan = this.querySelector('.vote-nogood-text'); const isVoted = textSpan.textContent === '싫어요됨'; if (this.disabled) return; this.disabled = true; this.classList.add('opacity-50'); fetch(`/questions/${wrId}/vote`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ type: 'nogood' }) }) .then(response => response.json()) .then(data => { if (data.success) { countSpan.textContent = data.nogood_count; if (data.user_vote === null) { // 취소된 경우 textSpan.textContent = '싫어요'; this.classList.remove('bg-orange-100', 'dark:bg-orange-900', 'text-orange-700', 'dark:text-orange-300'); this.classList.add('bg-red-100', 'dark:bg-red-900', 'text-red-700', 'dark:text-red-300', 'hover:bg-red-200', 'dark:hover:bg-red-800'); } else { // 투표된 경우 textSpan.textContent = '싫어요됨'; this.classList.remove('bg-red-100', 'dark:bg-red-900', 'text-red-700', 'dark:text-red-300', 'hover:bg-red-200', 'dark:hover:bg-red-800'); this.classList.add('bg-orange-100', 'dark:bg-orange-900', 'text-orange-700', 'dark:text-orange-300'); } console.log('Success message:', data.message); showMessage(data.message, 'success'); } else { console.log('Error message:', data.error); showMessage(data.error || '싫어요에 실패했습니다.', 'error'); } this.disabled = false; this.classList.remove('opacity-50'); }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); this.disabled = false; this.classList.remove('opacity-50'); }); }); } // 스크랩 기능 const scrapBtn = document.getElementById('scrap-btn'); if (scrapBtn) { scrapBtn.addEventListener('click', function() { const wrId = this.dataset.wrId; const countSpan = this.querySelector('.scrap-count'); this.disabled = true; this.classList.add('opacity-50'); fetch(`/questions/${wrId}/scrap`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { countSpan.textContent = `(${data.count})`; const textSpan = this.querySelector('.scrap-text'); const iconSpan = this.querySelector('i'); // 스크랩된 상태로 변경 textSpan.textContent = '스크랩됨'; iconSpan.className = 'fa fa-bookmark mr-1'; this.classList.remove('bg-gray-100', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300', 'hover:bg-gray-200', 'dark:hover:bg-gray-600'); this.classList.add('bg-yellow-100', 'dark:bg-yellow-900', 'text-yellow-700', 'dark:text-yellow-300'); // 스크랩 후 버튼 비활성화 this.disabled = true; showMessage(data.message, 'success'); } else { showMessage(data.error || '스크랩에 실패했습니다.', 'error'); } this.disabled = false; this.classList.remove('opacity-50'); }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); this.disabled = false; this.classList.remove('opacity-50'); }); }); } // 신고 기능 document.querySelectorAll('button[id^=qa_singo-]').forEach(button => { button.addEventListener('click', function() { const idParts = this.id.split('-'); const bo_table = idParts[1]; const wr_id = idParts[2]; const radios_reason = ["#rdo_reason_ad", "#rdo_reason_ob", "#rdo_reason_ha", "#rdo_reason_etc"]; // 모달 다이얼로그 표시 const modal = document.getElementById('sir_react_singod'); modal.style.display = 'block'; modal.style.position = 'fixed'; modal.style.top = '50%'; modal.style.left = '50%'; modal.style.transform = 'translate(-50%, -50%)'; modal.style.backgroundColor = 'white'; modal.style.padding = '20px'; modal.style.borderRadius = '8px'; modal.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)'; modal.style.zIndex = '1000'; modal.style.minWidth = '400px'; // 배경 오버레이 const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; overlay.style.zIndex = '999'; document.body.appendChild(overlay); // 신고하기 버튼 const reportBtn = document.createElement('button'); reportBtn.textContent = '신고하기'; reportBtn.className = 'px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 mr-2'; reportBtn.addEventListener('click', function() { let r_checked = false; let reason = ''; radios_reason.forEach(radioId => { if (document.querySelector(radioId).checked) { r_checked = true; if (radioId === '#rdo_reason_etc') { const customReason = document.getElementById('sg_reason').value.trim(); if (customReason.length < 10) { alert('기타 선택시 신고사유를 10글자 이상 입력하십시오.'); return; } reason = customReason; } else { reason = document.querySelector(radioId).value; } } }); if (!r_checked) { alert('신고사유를 선택하세요.'); return; } // 신고 요청 fetch(`/questions/${wr_id}/singo`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ bo_table: bo_table, reason: reason, q_mode: 'singo' }) }) .then(response => response.json()) .then(data => { if (data.error) { alert(data.error); return; } // 모달 닫기 modal.style.display = 'none'; document.body.removeChild(overlay); // 신고 카운트 업데이트 const countElement = document.querySelector(`#qa_singo-${bo_table}-${wr_id} .singo-count`); if (countElement) { countElement.textContent = data.count; } // 버튼 비활성화 button.disabled = true; button.classList.add('opacity-50'); alert(data.message); }) .catch(error => { console.error('Error:', error); alert('신고 처리 중 오류가 발생했습니다.'); }); }); // 취소 버튼 const cancelBtn = document.createElement('button'); cancelBtn.textContent = '취소'; cancelBtn.className = 'px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600'; cancelBtn.addEventListener('click', function() { modal.style.display = 'none'; document.body.removeChild(overlay); }); // 기존 버튼들 제거 const existingButtons = modal.querySelectorAll('button'); existingButtons.forEach(btn => btn.remove()); // 버튼들 추가 const buttonContainer = document.createElement('div'); buttonContainer.className = 'mt-4 text-right'; buttonContainer.appendChild(reportBtn); buttonContainer.appendChild(cancelBtn); modal.appendChild(buttonContainer); // 오버레이 클릭 시 닫기 overlay.addEventListener('click', function() { modal.style.display = 'none'; document.body.removeChild(overlay); }); }); }); // 공유 기능 const shareBtn = document.getElementById('share-btn'); if (shareBtn) { shareBtn.addEventListener('click', function() { const shareModal = document.getElementById('share-modal'); const currentUrl = window.location.href; const title = document.title; // SNS 공유 링크 설정 const facebookUrl = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(currentUrl)}`; const twitterUrl = `https://twitter.com/intent/tweet?url=${encodeURIComponent(currentUrl)}&text=${encodeURIComponent(title)}`; document.getElementById('facebook-share').href = facebookUrl; document.getElementById('twitter-share').href = twitterUrl; shareModal.classList.remove('hidden'); }); } // 메시지 표시 함수 }); // 공유 모달 닫기 function closeShareModal() { document.getElementById('share-modal').classList.add('hidden'); } // 클립보드에 복사 function copyToClipboard() { navigator.clipboard.writeText(window.location.href).then(function() { showMessage('링크가 클립보드에 복사되었습니다.', 'success'); closeShareModal(); }); } // Image preview function function previewImage(imageUrl, filename) { // Create modal overlay const overlay = document.createElement('div'); overlay.className = 'fixed inset-0 bg-black bg-opacity-75 z-50 flex items-center justify-center p-4'; overlay.onclick = function() { document.body.removeChild(overlay); }; // Create modal content const modal = document.createElement('div'); modal.className = 'bg-white dark:bg-gray-800 rounded-lg max-w-4xl max-h-full overflow-auto'; modal.onclick = function(e) { e.stopPropagation(); }; modal.innerHTML = ` <div class="p-4 border-b border-gray-200 dark:border-gray-700"> <div class="flex items-center justify-between"> <h3 class="text-lg font-medium text-gray-900 dark:text-gray-100">${filename}</h3> <button onclick="document.body.removeChild(this.closest('.fixed'))" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> </svg> </button> </div> </div> <div class="p-4"> <img src="${imageUrl}" alt="${filename}" class="max-w-full h-auto rounded-lg shadow-lg"> </div> `; overlay.appendChild(modal); document.body.appendChild(overlay); } // 메시지 표시 함수 function showMessage(message, type = 'info') { console.log('showMessage called:', message, type); // 기존 메시지 제거 const existingMessage = document.querySelector('.message-toast'); if (existingMessage) { existingMessage.remove(); } // 새 메시지 생성 const messageDiv = document.createElement('div'); messageDiv.className = 'message-toast fixed top-4 right-4 z-50 px-6 py-3 rounded-lg shadow-lg transition-all duration-300'; if (type === 'success') { messageDiv.className += ' bg-green-500 text-white'; } else if (type === 'error') { messageDiv.className += ' bg-red-500 text-white'; } else { messageDiv.className += ' bg-blue-500 text-white'; } messageDiv.textContent = message; document.body.appendChild(messageDiv); console.log('Message displayed:', messageDiv); // 3초 후 자동 제거 setTimeout(() => { if (messageDiv.parentNode) { messageDiv.style.opacity = '0'; messageDiv.style.transform = 'translateX(100%)'; setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 300); } }, 3000); } // 답변 작성 폼 처리 document.addEventListener('DOMContentLoaded', function() { // 답변 작성 폼 const answerForm = document.getElementById('answer-form-element'); if (answerForm) { answerForm.addEventListener('submit', function(e) { e.preventDefault(); // 기본 폼 제출 방지 // 중복 제출 방지 const submitBtn = this.querySelector('button[type="submit"]'); if (submitBtn.disabled) { return; } // 버튼 비활성화 submitBtn.disabled = true; submitBtn.textContent = '등록 중...'; // CKEditor 내용 가져오기 const content = CKEDITOR.instances.wr_content ? CKEDITOR.instances.wr_content.getData() : ''; // FormData 생성 const formData = new FormData(); formData.append('_token', document.querySelector('input[name="_token"]').value); formData.append('wr_qa_id', document.querySelector('input[name="wr_qa_id"]').value); formData.append('wr_content', content); // AJAX 요청 fetch('https://new-sir.gnuboard.net/questions/answer', { method: 'POST', body: formData, headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }) .then(response => response.json()) .then(data => { if (data.success) { // 성공 메시지 표시 showMessage('답변이 등록되었습니다.', 'success'); // 페이지 새로고침하여 새 답변 표시 window.location.reload(); } else { // 에러 메시지 표시 showMessage(data.message || '답변 등록 중 오류가 발생했습니다.', 'error'); // 버튼 복원 submitBtn.disabled = false; submitBtn.textContent = '답변 등록'; } }) .catch(error => { console.error('Error:', error); showMessage('답변 등록 중 오류가 발생했습니다.', 'error'); // 버튼 복원 submitBtn.disabled = false; submitBtn.textContent = '답변 등록'; }); }); } // URL 프래그먼트 처리 function handleFragment() { const hash = window.location.hash; if (hash === '#new-answer') { // URL에서 프래그먼트 제거 (히스토리 정리) history.replaceState(null, null, window.location.pathname + window.location.search); setTimeout(() => { // 답변 목록에서 가장 최근 답변 찾기 const answers = document.querySelectorAll('.answer-item'); if (answers.length > 0) { const lastAnswer = answers[answers.length - 1]; lastAnswer.scrollIntoView({ behavior: 'smooth', block: 'start' }); // 새 답변에 하이라이트 효과 추가 lastAnswer.style.backgroundColor = '#fef3c7'; lastAnswer.style.transition = 'background-color 0.3s ease'; setTimeout(() => { lastAnswer.style.backgroundColor = ''; }, 3000); } }, 500); } } // 페이지 로드 시 프래그먼트 처리 handleFragment(); // 해시 변경 이벤트 리스너 (브라우저 뒤로가기/앞으로가기 대응) window.addEventListener('hashchange', handleFragment); }); // 답변 좋아요/싫어요 기능 document.addEventListener('DOMContentLoaded', function() { // 답변 좋아요 버튼 document.querySelectorAll('.vote-good-btn').forEach(button => { if (button.id !== 'vote-good-btn') { // 질문 좋아요 버튼 제외 button.addEventListener('click', function() { const wrId = this.dataset.wrId; const countSpan = this.querySelector('.vote-good-count'); const textSpan = this.querySelector('.vote-good-text'); const isVoted = textSpan.textContent === '좋아요됨'; if (this.disabled) return; this.disabled = true; this.classList.add('opacity-50'); fetch(`/questions/${wrId}/vote`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ type: 'good' }) }) .then(response => response.json()) .then(data => { if (data.success) { countSpan.textContent = data.good_count; if (data.user_vote === null) { // 취소된 경우 textSpan.textContent = '좋아요'; this.classList.remove('bg-blue-100', 'dark:bg-blue-700', 'text-blue-700', 'dark:text-blue-300'); this.classList.add('bg-gray-100', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300', 'hover:bg-gray-200', 'dark:hover:bg-gray-600'); } else { // 투표된 경우 textSpan.textContent = '좋아요됨'; this.classList.remove('bg-gray-100', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300', 'hover:bg-gray-200', 'dark:hover:bg-gray-600'); this.classList.add('bg-blue-100', 'dark:bg-blue-700', 'text-blue-700', 'dark:text-blue-300'); } showMessage(data.message, 'success'); } else { showMessage(data.error || '좋아요에 실패했습니다.', 'error'); } this.disabled = false; this.classList.remove('opacity-50'); }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); this.disabled = false; this.classList.remove('opacity-50'); }); }); } }); // 답변 싫어요 버튼 document.querySelectorAll('.vote-nogood-btn').forEach(button => { if (button.id !== 'vote-nogood-btn') { // 질문 싫어요 버튼 제외 button.addEventListener('click', function() { const wrId = this.dataset.wrId; const countSpan = this.querySelector('.vote-nogood-count'); const textSpan = this.querySelector('.vote-nogood-text'); const isVoted = textSpan.textContent === '싫어요됨'; if (this.disabled) return; this.disabled = true; this.classList.add('opacity-50'); fetch(`/questions/${wrId}/vote`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ type: 'nogood' }) }) .then(response => response.json()) .then(data => { if (data.error === '') { countSpan.textContent = data.count; if (data.cancle_vote === 'cancle') { // 취소된 경우 textSpan.textContent = '싫어요'; this.classList.remove('bg-orange-100', 'dark:bg-orange-700', 'text-orange-700', 'dark:text-orange-300'); this.classList.add('bg-gray-100', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300', 'hover:bg-gray-200', 'dark:hover:bg-gray-600'); } else { // 투표된 경우 textSpan.textContent = '싫어요됨'; this.classList.remove('bg-gray-100', 'dark:bg-gray-700', 'text-gray-700', 'dark:text-gray-300', 'hover:bg-gray-200', 'dark:hover:bg-gray-600'); this.classList.add('bg-orange-100', 'dark:bg-orange-700', 'text-orange-700', 'dark:text-orange-300'); } showMessage(data.message, 'success'); } else { showMessage(data.error || '싫어요에 실패했습니다.', 'error'); } this.disabled = false; this.classList.remove('opacity-50'); }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); this.disabled = false; this.classList.remove('opacity-50'); }); }); } }); // 채택 버튼 document.querySelectorAll('.adopt-btn').forEach(button => { button.addEventListener('click', function() { const questionId = this.dataset.questionId; const answerId = this.dataset.answerId; if (!confirm('이 답변을 채택하시겠습니까?\n\n채택된 답변은 변경할 수 없습니다.')) { return; } fetch(`/questions/${questionId}/adopt/${answerId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage(data.message, 'success'); // 페이지 새로고침하여 채택 상태 반영 setTimeout(() => { location.reload(); }, 1500); } else { showMessage(data.error || '채택에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); }); }); }); // 채택 취소 버튼 document.querySelectorAll('.unadopt-btn').forEach(button => { button.addEventListener('click', function() { const questionId = this.dataset.questionId; if (!confirm('채택을 취소하시겠습니까?\n\n채택된 답변 작성자에게 지급된 포인트가 회수됩니다.')) { return; } fetch(`/questions/${questionId}/unadopt`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage(data.message, 'success'); // 페이지 새로고침하여 채택 취소 상태 반영 setTimeout(() => { location.reload(); }, 1500); } else { showMessage(data.error || '채택 취소에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); }); }); }); // 댓글 작성 document.querySelectorAll('.comment-form-element').forEach(form => { form.addEventListener('submit', function(e) { e.preventDefault(); // 중복 제출 방지 const submitBtn = this.querySelector('button[type="submit"]'); if (submitBtn.disabled) { return; } const parentId = this.dataset.parentId; const content = this.querySelector('textarea[name="wr_content"]').value.trim(); if (!content) { showMessage('댓글 내용을 입력해주세요.', 'error'); return; } // 버튼 비활성화 submitBtn.disabled = true; submitBtn.textContent = '등록 중...'; const formData = new FormData(); formData.append('wr_qa_id', parentId); formData.append('wr_content', content); formData.append('_token', document.querySelector('meta[name="csrf-token"]').getAttribute('content')); fetch('/questions/comment', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { showMessage(data.message, 'success'); // 페이지 새로고침하여 댓글 반영 setTimeout(() => { location.reload(); }, 1000); } else { showMessage(data.error || '댓글 등록에 실패했습니다.', 'error'); // 실패 시 버튼 다시 활성화 submitBtn.disabled = false; submitBtn.textContent = '댓글 등록'; } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); // 오류 시 버튼 다시 활성화 submitBtn.disabled = false; submitBtn.textContent = '댓글 등록'; }); }); }); // 댓글 수정 document.querySelectorAll('.edit-comment-btn').forEach(button => { button.addEventListener('click', function() { const commentContainer = this.closest('.relative'); const commentContent = commentContainer.querySelector('.comment-content'); const editForm = commentContainer.querySelector('.comment-edit-form'); const textarea = editForm.querySelector('textarea'); // 편집 모드로 전환 commentContent.classList.add('hidden'); editForm.classList.remove('hidden'); textarea.focus(); }); }); // 댓글 저장 document.querySelectorAll('.comment-save-btn').forEach(button => { button.addEventListener('click', function() { const commentId = this.dataset.commentId; const commentContainer = this.closest('.relative'); const textarea = commentContainer.querySelector('textarea'); const newContent = textarea.value.trim(); if (!newContent) { showMessage('댓글 내용을 입력해주세요.', 'error'); return; } fetch(`/questions/comment/${commentId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ wr_content: newContent }) }) .then(response => response.json()) .then(data => { if (data.success) { showMessage(data.message, 'success'); // 페이지 새로고침하여 수정된 댓글 반영 setTimeout(() => { location.reload(); }, 1000); } else { showMessage(data.error || '댓글 수정에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); }); }); }); // 댓글 수정 취소 document.querySelectorAll('.comment-cancel-btn').forEach(button => { button.addEventListener('click', function() { const commentContainer = this.closest('.relative'); const commentContent = commentContainer.querySelector('.comment-content'); const editForm = commentContainer.querySelector('.comment-edit-form'); const textarea = editForm.querySelector('textarea'); // 원래 내용으로 복원 textarea.value = commentContent.textContent.trim(); // 편집 모드 해제 editForm.classList.add('hidden'); commentContent.classList.remove('hidden'); }); }); // 댓글 삭제 document.querySelectorAll('.delete-comment-btn').forEach(button => { button.addEventListener('click', function() { const commentId = this.dataset.commentId; if (!confirm('정말로 이 댓글을 삭제하시겠습니까?')) { return; } fetch(`/questions/comment/${commentId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage(data.message, 'success'); // 페이지 새로고침하여 삭제된 댓글 반영 setTimeout(() => { location.reload(); }, 1000); } else { showMessage(data.error || '댓글 삭제에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); }); }); }); // 대댓글 답글 버튼 클릭 document.querySelectorAll('.reply-comment-btn').forEach(button => { button.addEventListener('click', function() { const commentId = this.getAttribute('data-comment-id'); const commentAuthor = this.getAttribute('data-comment-author'); const replyForm = this.closest('.relative').querySelector('.reply-form'); const replyTarget = replyForm.querySelector('.reply-target'); // 다른 대댓글 폼들 숨기기 document.querySelectorAll('.reply-form').forEach(form => { if (form !== replyForm) { form.classList.add('hidden'); } }); // 현재 대댓글 폼 토글 replyForm.classList.toggle('hidden'); // 답글 대상 표시 replyTarget.textContent = `${commentAuthor}님에게 답글`; // 폼이 보이면 textarea에 포커스 if (!replyForm.classList.contains('hidden')) { const textarea = replyForm.querySelector('textarea'); textarea.focus(); } }); }); // 대댓글 취소 버튼 document.querySelectorAll('.reply-cancel-btn').forEach(button => { button.addEventListener('click', function() { const replyForm = this.closest('.reply-form'); const textarea = replyForm.querySelector('textarea'); // 내용 초기화 textarea.value = ''; // 폼 숨기기 replyForm.classList.add('hidden'); }); }); // 대댓글 등록 document.querySelectorAll('.reply-form-element').forEach(form => { form.addEventListener('submit', function(e) { e.preventDefault(); // 중복 제출 방지 const submitBtn = this.querySelector('button[type="submit"]'); if (submitBtn.disabled) { return; } const formData = new FormData(this); const parentId = this.getAttribute('data-parent-id'); const commentId = this.getAttribute('data-comment-id'); const textarea = this.querySelector('textarea[name="wr_content"]'); const content = textarea.value.trim(); if (!content) { showMessage('답글 내용을 입력해주세요.', 'error'); return; } // 버튼 비활성화 submitBtn.disabled = true; submitBtn.textContent = '등록 중...'; // 답글 대상 정보 추가 formData.append('wr_comment_reply', commentId); formData.append('wr_qa_id', parentId); formData.append('parent_id', parentId); fetch(`/questions/comment`, { method: 'POST', body: formData, headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage('답글이 등록되었습니다.', 'success'); textarea.value = ''; this.closest('.reply-form').classList.add('hidden'); // 페이지 새로고침으로 새 답글 표시 setTimeout(() => { location.reload(); }, 1000); } else { showMessage(data.error || '답글 등록 중 오류가 발생했습니다.', 'error'); // 실패 시 버튼 다시 활성화 submitBtn.disabled = false; submitBtn.textContent = '답글 등록'; } }) .catch(error => { console.error('Error:', error); showMessage('네트워크 오류가 발생했습니다.', 'error'); // 오류 시 버튼 다시 활성화 submitBtn.disabled = false; submitBtn.textContent = '답글 등록'; }); }); }); }); // 프래그먼트 식별자 스크롤 처리 document.addEventListener('DOMContentLoaded', function() { // URL에 프래그먼트가 있는지 확인 if (window.location.hash) { const hash = window.location.hash.substring(1); // # 제거 const targetElement = document.getElementById(hash); if (targetElement) { // 약간의 지연 후 스크롤 (페이지 로딩 완료 후) setTimeout(() => { const headerHeight = 120; // 헤더 높이 + 여백 const elementPosition = targetElement.offsetTop; const offsetPosition = elementPosition - headerHeight; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); }, 100); } } }); // 답변 정렬 기능 document.addEventListener('DOMContentLoaded', function() { const sortSelect = document.getElementById('qa_sort_select'); if (sortSelect) { sortSelect.addEventListener('change', function() { const sortValue = this.value; const currentUrl = new URL(window.location); currentUrl.searchParams.set('vsst', sortValue); currentUrl.searchParams.set('vpage', '1'); // 정렬 변경 시 첫 페이지로 currentUrl.hash = '#qa_answer'; window.location.href = currentUrl.toString(); }); } }); // 답변 삭제 기능 document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.delete-answer-btn').forEach(button => { button.addEventListener('click', function() { const answerId = this.getAttribute('data-answer-id'); if (confirm('정말로 이 답변을 삭제하시겠습니까?')) { fetch(`/questions/${answerId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') } }) .then(response => response.json()) .then(data => { if (data.success) { // 답변 요소 제거 const answerElement = document.getElementById(`answer_${answerId}`); if (answerElement) { answerElement.remove(); } // 답변 수 업데이트 (전체 답변 수에서 1 감소) const answerCountElement = document.querySelector('h2'); if (answerCountElement) { const currentCount = parseInt(answerCountElement.textContent.match(/\d+/)[0]); answerCountElement.textContent = `답변 ${currentCount - 1}개`; } // 페이지 새로고침 (전체 답변 수와 페이징을 정확히 반영하기 위해) setTimeout(() => { window.location.reload(); }, 1000); showMessage('답변이 삭제되었습니다.', 'success'); } else { showMessage(data.message || '답변 삭제 중 오류가 발생했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('답변 삭제 중 오류가 발생했습니다.', 'error'); }); } }); }); }); // 미채택 완료 function closeWithoutAdopt(questionId) { if (!confirm('답변을 채택하지 않고 질문을 완료하시겠습니까?\n\n이 작업은 취소할 수 없습니다.')) { return; } fetch(`/questions/${questionId}/close-without-adopt`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage('질문이 미채택 완료되었습니다.', 'success'); setTimeout(() => { window.location.reload(); }, 1500); } else { showMessage(data.error || '처리에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('처리 중 오류가 발생했습니다.', 'error'); }); } // 재등록 function reregisterQuestion(questionId) { if (!confirm('이 질문을 재등록하시겠습니까?\n\n재등록하면 새로운 답변을 받을 수 있습니다.')) { return; } fetch(`/questions/${questionId}/reregister`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type': 'application/json', 'Accept': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { showMessage('질문이 재등록되었습니다.', 'success'); if (data.redirect) { setTimeout(() => { window.location.href = data.redirect; }, 1500); } else { setTimeout(() => { window.location.reload(); }, 1500); } } else { showMessage(data.error || '처리에 실패했습니다.', 'error'); } }) .catch(error => { console.error('Error:', error); showMessage('처리 중 오류가 발생했습니다.', 'error'); }); } // Prism.js용 코드 복사 기능 function copyCodePrism(button) { // 버튼의 부모 요소에서 코드 블록 찾기 const wrapper = button.closest('.code-block-wrapper'); const codeBlock = wrapper.querySelector('code'); const codeText = codeBlock.textContent; // 클립보드에 복사 navigator.clipboard.writeText(codeText).then(() => { // 버튼 텍스트 임시 변경 const originalHTML = button.innerHTML; const originalStyle = { background: button.style.background, borderColor: button.style.borderColor, color: button.style.color }; button.innerHTML = '<svg style="width: 14px; height: 14px; display: inline-block; vertical-align: middle; margin-right: 4px;" fill="currentColor" viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></svg> Copied!'; button.style.background = 'rgba(72, 187, 120, 0.7)'; button.style.borderColor = 'rgba(72, 187, 120, 0.9)'; button.style.color = '#fff'; // 2초 후 원래 상태로 복구 setTimeout(() => { button.innerHTML = originalHTML; button.style.background = originalStyle.background; button.style.borderColor = originalStyle.borderColor; button.style.color = originalStyle.color; }, 2000); }).catch(err => { console.error('Failed to copy code:', err); // Fallback: 전통적인 복사 방법 사용 const textArea = document.createElement('textarea'); textArea.value = codeText; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); // 성공 표시 const originalHTML = button.innerHTML; button.innerHTML = '<span style="margin-right: 4px;">✓</span> Copied!'; setTimeout(() => { button.innerHTML = originalHTML; }, 2000); } catch (err) { alert('코드 복사에 실패했습니다.'); } document.body.removeChild(textArea); }); } </script> </main> <!-- Footer --> <footer class="bg-brand-bg-light dark:bg-brand-bg-dark"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> <div class="flex flex-col sm:flex-row justify-between items-center gap-4"> <!-- Footer Text (Left/Center) --> <div class="text-center sm:text-left text-sm text-brand-neutral-light dark:text-brand-neutral-dark"> <p class="hidden md:block">(주)에스아이알소프트 | 사업자등록번호: 217-81-36347 | 대표: 홍석명</p> <p class="hidden md:block mt-1">서울특별시 강남구 테헤란로 322 한신인터밸리24 서관 1402호</p> <p class="mt-3 font-semibold">© 2025 SIRSOFT. All rights reserved.</p> </div> </div> </div> </footer> <!-- Floating Action Buttons --> <script> function toggleDarkMode() { const html = document.documentElement; const isDark = html.classList.contains('dark'); if (isDark) { html.classList.remove('dark'); localStorage.setItem('darkMode', 'false'); } else { html.classList.add('dark'); localStorage.setItem('darkMode', 'true'); } } function toggleProfileMenu() { const dropdown = document.getElementById('profileDropdown'); dropdown.classList.toggle('hidden'); } // Close profile dropdown when clicking outside document.addEventListener('click', function(event) { const dropdown = document.getElementById('profileDropdown'); const button = event.target.closest('button[onclick="toggleProfileMenu()"]'); if (!button && !dropdown?.contains(event.target)) { dropdown?.classList.add('hidden'); } }); function toggleMobileMenu() { const mobileMenu = document.getElementById('mobileMenu'); const hamburgerIcon = document.getElementById('hamburgerIcon'); const closeIcon = document.getElementById('closeIcon'); // 요소가 존재하는지 확인 if (!mobileMenu || !hamburgerIcon || !closeIcon) { console.error('Mobile menu elements not found:', { mobileMenu: !!mobileMenu, hamburgerIcon: !!hamburgerIcon, closeIcon: !!closeIcon }); return; } if (mobileMenu.classList.contains('hidden')) { // Open menu mobileMenu.classList.remove('hidden'); mobileMenu.style.opacity = '0'; mobileMenu.style.transform = 'translateY(-10px)'; setTimeout(() => { mobileMenu.style.transition = 'opacity 200ms ease-out, transform 200ms ease-out'; mobileMenu.style.opacity = '1'; mobileMenu.style.transform = 'translateY(0)'; }, 10); // Switch icons hamburgerIcon.classList.add('hidden'); closeIcon.classList.remove('hidden'); } else { // Close menu mobileMenu.style.opacity = '0'; mobileMenu.style.transform = 'translateY(-10px)'; setTimeout(() => { mobileMenu.classList.add('hidden'); mobileMenu.style.transition = ''; }, 200); // Switch icons hamburgerIcon.classList.remove('hidden'); closeIcon.classList.add('hidden'); } } function closeMobileMenu() { const mobileMenu = document.getElementById('mobileMenu'); const hamburgerIcon = document.getElementById('hamburgerIcon'); const closeIcon = document.getElementById('closeIcon'); mobileMenu?.classList.add('hidden'); hamburgerIcon?.classList.remove('hidden'); closeIcon?.classList.add('hidden'); // 모든 서브메뉴 닫기 document.querySelectorAll('.submenu').forEach(submenu => { submenu.style.maxHeight = '0'; }); document.querySelectorAll('.mobile-menu-item button svg').forEach(arrow => { arrow.classList.remove('rotate-180'); }); } function toggleMobileSubmenu(button) { // 부모 컨테이너에서 submenu 찾기 (버튼의 부모의 다음 형제) const parent = button.closest('.mobile-menu-item'); const submenu = parent.querySelector('.submenu'); const arrow = button.querySelector('svg'); if (!submenu) return; // 현재 상태 확인 const isOpen = submenu.style.maxHeight && submenu.style.maxHeight !== '0px'; if (isOpen) { // 닫기: max-height를 0으로 submenu.style.maxHeight = '0'; arrow.classList.remove('rotate-180'); } else { // 먼저 다른 열린 서브메뉴들을 모두 닫기 (한 번에 하나만 열리도록) document.querySelectorAll('.submenu').forEach(otherSubmenu => { if (otherSubmenu !== submenu) { otherSubmenu.style.maxHeight = '0'; const otherArrow = otherSubmenu.closest('.mobile-menu-item')?.querySelector('button svg'); if (otherArrow) { otherArrow.classList.remove('rotate-180'); } } }); // 열기: 실제 높이로 max-height 설정 const innerDiv = submenu.querySelector('div'); submenu.style.maxHeight = innerDiv.scrollHeight + 'px'; arrow.classList.add('rotate-180'); } } // Close mobile menu when clicking outside document.addEventListener('click', function(event) { const mobileMenu = document.getElementById('mobileMenu'); const menuButton = event.target.closest('button[onclick="toggleMobileMenu()"]'); const menuLink = event.target.closest('#mobileMenu'); if (!menuButton && !menuLink && !mobileMenu?.classList.contains('hidden')) { closeMobileMenu(); } }); </script> <!-- Alpine.js --> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- Floating Action Buttons --> <!-- Attendance Check Float Button --> <!-- Prism.js JavaScript --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script> <!-- Core languages --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-clike.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script> <!-- PHP requires markup-templating --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup-templating.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-php.min.js"></script> <!-- Other languages --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-sql.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script> <!-- Plugins --> <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script> <script> // Prism.js 초기화 및 하이라이팅 새로고침 document.addEventListener('DOMContentLoaded', function() { // Prism.js 하이라이팅 적용 if (typeof Prism !== 'undefined') { Prism.highlightAll(); } }); </script> </body> </html>