'fail','message'=>'댓글 또는 이미지가 필요합니다.'], 400); } if (mb_strlen($comment,'UTF-8') > 2000) { out(['status'=>'fail','message'=>'댓글은 2,000자 이내로 입력하세요.'], 400); } /* ✅ DB 연결 */ try{ $pdo = new PDO( "mysql:host=db;dbname=egbim;charset=utf8mb4", 'egbim','baron3840!!', [ PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC, ] ); $pdo->exec("SET time_zone = '+09:00'"); }catch(Exception $e){ out(['status'=>'fail','message'=>'DB 연결 실패: '.$e->getMessage()],500); } /* ✅ 글 존재 확인 */ $st = $pdo->prepare("SELECT post_id, login_id AS author_id, is_secret FROM qa_posts WHERE post_id=?"); $st->execute([$postId]); $post = $st->fetch(); if (!$post) out(['status'=>'fail','message'=>'존재하지 않는 글입니다.'],404); if (!$isAdmin && (int)$post['is_secret'] === 1 && $post['author_id'] !== $loginId) { out(['status'=>'fail','message'=>'비밀글에는 작성자 본인과 관리자만 댓글을 작성할 수 있습니다.'],403); } /* ✅ 로그인 이름 폴백 */ if ($loginId === '') $loginId = 'guest'; if ($userName === '') $userName = $loginId; /* ✅ 댓글 INSERT */ try { $ins = $pdo->prepare(" INSERT INTO qa_comments (post_id, commenter, user_name, content, created_at) VALUES (:pid, :login_id, :user_name, :content, NOW()) "); $ins->execute([ ':pid' => $postId, ':login_id' => $loginId, ':user_name' => $userName, ':content' => $comment, ]); $cid = (int)$pdo->lastInsertId(); } catch (Exception $e) { out(['status'=>'fail','message'=>'댓글 저장 실패: '.$e->getMessage()],500); } /* ================================ 댓글 이미지 업로드 + 썸네일 생성 ================================ */ $uploadedPaths = []; $thumbPaths = []; $uploadDir = $_SERVER['DOCUMENT_ROOT'].'/egbim/uploads/comment/'; $thumbDir = $uploadDir.'thumb/'; $uploadUrl = '/uploads/comment/'; $thumbUrl = '/uploads/comment/thumb/'; if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true); if (!is_dir($thumbDir)) mkdir($thumbDir, 0777, true); // 🔧 썸네일 생성 함수 function create_thumbnail($src, $dest, $target_width = 180) { $info = getimagesize($src); if (!$info) return false; switch ($info[2]) { case IMAGETYPE_JPEG: $img = imagecreatefromjpeg($src); break; case IMAGETYPE_PNG: $img = imagecreatefrompng($src); break; case IMAGETYPE_GIF: $img = imagecreatefromgif($src); break; case IMAGETYPE_WEBP: $img = imagecreatefromwebp($src); break; default: return false; } $width = $info[0]; $height = $info[1]; $target_height = intval($height * ($target_width / $width)); $thumb = imagecreatetruecolor($target_width, $target_height); imagecopyresampled($thumb, $img, 0, 0, 0, 0, $target_width, $target_height, $width, $height ); imagejpeg($thumb, $dest, 90); return true; } $imageList = []; // 🔧 이미지 저장 if (!empty($_FILES['images']['name'][0])) { foreach ($_FILES['images']['tmp_name'] as $i => $tmpName) { if (!is_uploaded_file($tmpName)) continue; $ext = strtolower(pathinfo($_FILES['images']['name'][$i], PATHINFO_EXTENSION)); if (!in_array($ext, ['jpg','jpeg','png','gif','webp'])) continue; $newName = time().'_'.bin2hex(random_bytes(3)).'.'.$ext; $dest = $uploadDir.$newName; $thumbName = 'thumb_'.$newName; $thumbDest = $thumbDir.$thumbName; // 원본 저장 if (move_uploaded_file($tmpName, $dest)) { chmod($dest, 0644); // 🔥 썸네일 생성 create_thumbnail($dest, $thumbDest, 180); $uploadedPaths[] = $uploadUrl.$newName; $thumbPaths[] = $thumbUrl.$thumbName; } } /* 🔧 DB 저장 */ $imgStmt = $pdo->prepare(" INSERT INTO qa_comment_images (comment_id, file_name, file_path, thumb_path) VALUES (:cid, :name, :path, :thumb) "); foreach ($uploadedPaths as $i => $url) { $imgStmt->execute([ ':cid' => $cid, ':name' => $_FILES['images']['name'][$i], ':path' => $url, ':thumb'=> $thumbPaths[$i] ]); /* 응답용 데이터 */ $imageList[] = [ 'full' => $url, 'thumb' => $thumbPaths[$i], 'name' => $_FILES['images']['name'][$i] ]; } } /* ✅ 성공 응답 */ out([ 'status' => 'ok', 'comment_id' => $cid, 'login_id' => $loginId, 'user_name' => $userName, 'comment_text' => $comment, 'created_at' => (new DateTime('now', new DateTimeZone('Asia/Seoul')))->format('Y-m-d H:i:s'), // 'images' => $uploadedPaths, 'images' => $imageList ]);