대량으로 팔지 않습니다. 저희 밭에서 직접 기르고, 아는 분들에게 직접 전해드리는 만큼 하나하나 정성을 담습니다. 오늘 딴 것을 오늘 보내드려요.
경남 밀양에서 3대째 작은 밭을 일구고 있습니다. 큰 유통은 하지 않고, 한 번 드셔보신 분들이 소개해주신 분들께만 조금씩 나눠드리고 있어요. 농약을 최소화하고, 수확한 날 바로 포장해서 보내드리는 것이 저희 방식입니다.
계절과 기후에 따라 품목과 수량이 조금씩 변동될 수 있습니다. 문의해 주시면 실시간 재고 확인해 드립니다.
중간 거래 마진 없는 산지 직거래 방식으로 안전하고 간결하게 고객 맞춤 운영을 실천합니다.
이달의 농산물에서 원하시는 품목과 수량을 확인해주세요. 실시간 조율을 적극 도와드립니다.
아래 주문폼 양식을 기재해 전송해 주시면 바로 실시간 문자로 소통해 안내 드립니다.
밭에서 싱싱하게 수확한 직후 꼼꼼하게 완충 포장하여 배송지까지 신속 전달합니다.
후기 등록은 회원가입 후 로그인하신 분만 가능합니다.
직접 소통을 원하신다면 연락 주셔도 되고, 아래 신청폼에 원하시는 품목을 작성해 주시면 꼼꼼하게 처리해 드립니다.
🔒 온라인 신청 및 문의는 회원가입한 분만 이용하실 수 있습니다.
번거로운 절차 없이 간단한 정보만으로 빠르게 가입하실 수 있어요.
일반 회원 로그인과 완전히 분리된 관리자 전용 입구입니다. 관리자 계정만 접속할 수 있습니다.
데모용 기본 비밀번호(admin123)를 계속 사용하는 것은 안전하지 않습니다. 접속 확인 후 반드시 변경해 주세요.
접수 완료된 소중한 제철 농산물 문의 내역 목록입니다.
| 번호 | 품목 | 문의 및 주소지 요약 | 상태 | 신청 날짜 | 제어 |
|---|
회원분들의 주문 신청과 데이터베이스 환경 상태를 안전하게 점검하고 모니터링합니다.
| 번호 | 주문자명 | 연락처 | 신청 품목 | 문의/주소 상세 | 상태 변경 | 조정 |
|---|
| 번호 | 아이디 | 이름 | 연락처 | 권한 | 가입일 | 조정 |
|---|
우측 원형 도장 문구
로고 이미지를 올리지 않으면 위 도장 글자 + 상호명 텍스트 로고가 표시됩니다.
| 후기 내용 | 작성자 표기 | 조정 |
|---|
사진을 올리지 않으면 아래 캡션 문구가 대신 표시됩니다.
| 항목 | 설명 | 조정 |
|---|
| 미리보기 | 이름 | 가격 | 배지 | 조정 |
|---|
phpMyAdmin 환경에서 jhbfarm 데이터베이스를 선택하고 아래 SQL 문장을 실행해 주셔야 로그인 및 주문 데이터가 실제 호스팅 서버에 정상 영구 보존됩니다.
-- 범이농원 닷홈 실제 연동용 데이터베이스 마이그레이션 스크립트
-- 닷홈 UTF-8 설정 필수 탑재
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`name` VARCHAR(50) NOT NULL,
`phone` VARCHAR(30) NOT NULL,
`role` VARCHAR(10) DEFAULT 'user',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `inquiries` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT NULL,
`name` VARCHAR(50) NOT NULL,
`phone` VARCHAR(30) NOT NULL,
`item` VARCHAR(100) NOT NULL,
`message` TEXT,
`status` VARCHAR(20) DEFAULT 'pending',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 농장 이야기 / 이달의 농산물 콘텐츠 저장용 (관리자 대시보드에서 등록/수정/삭제)
CREATE TABLE IF NOT EXISTS `site_content` (
`content_key` VARCHAR(50) PRIMARY KEY,
`content_value` LONGTEXT NOT NULL,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 농장 이야기 / 이달의 농산물 기본값 삽입 (이미 있으면 건드리지 않음)
INSERT INTO `site_content` (`content_key`, `content_value`)
SELECT 'about', '{"eyebrow": "농장 이야기", "title": "작은 밭에서, 아는 만큼만 정직하게", "description": "경남 밀양에서 3대째 작은 밭을 일구고 있습니다. 큰 유통은 하지 않고, 한 번 드셔보신 분들이 소개해주신 분들께만 조금씩 나눠드리고 있어요. 농약을 최소화하고, 수확한 날 바로 포장해서 보내드리는 것이 저희 방식입니다.", "photoCaption": "[ 범이농원 배추밭 전경 ]", "facts": [{"id": 1, "label": "재배 방식", "text": "저농약 · 노지 재배, 제철에만 한정 수확"}, {"id": 2, "label": "배송", "text": "수확 당일 직접 전달 또는 꼼꼼한 안전 우체국 택배"}, {"id": 3, "label": "규모", "text": "연간 소량 한정 재배로 늘 좋은 상태만 엄격 선별"}]}'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `site_content` WHERE `content_key` = 'about');
INSERT INTO `site_content` (`content_key`, `content_value`)
SELECT 'products', '[{"id": 1, "emoji": "🌾", "name": "햅쌀", "note": "직접 정성껏 도정한 신동진쌀. 주문 접수 즉시 신선 도정해 배송합니다.", "price": "4kg 22,000원", "badge": "한정"}, {"id": 2, "emoji": "🥬", "name": "제철 배추", "note": "김장철에 안성맞춤인, 속이 노랗고 꽉 찬 저농약 건강 배추입니다.", "price": "3포기 15,000원", "badge": "예약중"}, {"id": 3, "emoji": "🌶️", "name": "태양초 고춧가루", "note": "햇볕에 100% 자연 건조한 태양초 고춧가루 (매운맛/보통맛 선택 가능)", "price": "1kg 26,000원", "badge": "인기"}, {"id": 4, "emoji": "🫙", "name": "재래식 참기름", "note": "전통 솥에서 적정 온도로 정성껏 볶아 기름을 짠 진하고 고소한 국산 참기름입니다.", "price": "500ml 24,000원", "badge": "상시"}, {"id": 5, "emoji": "🍯", "name": "토종 야생꿀", "note": "밀양 산자락에서 건강하게 벌을 길러 채취한 깊고 진한 맛의 토종 밀봉 꿀입니다.", "price": "700g 45,000원", "badge": "소량"}, {"id": 6, "emoji": "🥔", "name": "강원/경남 햇감자", "note": "껍질이 얇고 부드럽고 쪄내면 포슬포슬함이 일품인 수미감자입니다.", "price": "5kg 18,000원", "badge": "상시"}]'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `site_content` WHERE `content_key` = 'products');
INSERT INTO `site_content` (`content_key`, `content_value`)
SELECT 'hero', '{"eyebrow": "소량 재배 · 직거래 판매", "title": "가까운 분들에게만\n정직하게 보내드리는\n제철 농산물입니다", "emphasis": "정직하게", "description": "대량으로 팔지 않습니다. 저희 밭에서 직접 기르고, 아는 분들에게 직접 전해드리는 만큼 하나하나 정성을 담습니다. 오늘 딴 것을 오늘 보내드려요.", "stampTop": "産地直送", "stampMid": "HYUNBEOM FARM", "stampBot": "범이농원"}'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `site_content` WHERE `content_key` = 'hero');
INSERT INTO `site_content` (`content_key`, `content_value`)
SELECT 'logo', '{"sealText": "産直", "brandText": "범이농원", "imageUrl": null}'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `site_content` WHERE `content_key` = 'logo');
INSERT INTO `site_content` (`content_key`, `content_value`)
SELECT 'testimonials', '{"eyebrow": "먼저 받아보신 분들", "title": "소개와 이웃의 신뢰로 자라온 농원", "items": [{"id": 1, "quote": "직접 수확하시는 모습을 전해 들어서 그런지 배송받아 김치를 담갔을 때 배추 본연의 아삭함이 훌륭했습니다.", "who": "— 이웃 입소문 박◯◯님"}, {"id": 2, "quote": "참기름 향이 정말 차원이 다릅니다. 고소한 내음이 온 집안에 진동을 하네요. 매번 이것만 찾게 됩니다.", "who": "— 단골 소개 김◯◯님"}, {"id": 3, "quote": "온라인 폼으로 신청하니 답장이 바로 오고, 다음 날 택배로 신선하게 안전 포장되어 와서 믿음이 백배 갑니다.", "who": "— 지인 소개 최◯◯님"}]}'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `site_content` WHERE `content_key` = 'testimonials');
-- 초기 마스터계정 사전 삽입 (관리자 아이디: admin, 암호: admin123)
-- 해시값은 PHP password_hash(PASSWORD_BCRYPT) 기준으로 실제 검증 완료된 값입니다.
INSERT INTO `users` (`username`, `password`, `name`, `phone`, `role`)
SELECT 'admin', '$2y$10$UNOV150dlzb8IflQtQL/BuDL4N8DlWXBCqK8uIUNv66QvaC4P02g.', '최고관리자', '010-5056-3007', 'admin'
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `users` WHERE `username` = 'admin');
PDO 객체 보안이 적용된 닷홈 맞춤형 API 스크립트입니다. $password 에 데이터베이스 실제 암호를 입력해야 연동됩니다.
<?php
/*
Dothome 호스팅 맞춤형 통합 백엔드 API (api.php)
경로: /hosting/jhbfarm/html/api.php
*/
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type");
session_start();
// PHP 경고/알림(Notice, Deprecated 등)이 JSON 응답 앞뒤에 섞여 나와
// "서버 응답 오류가 발생했습니다" 파싱 실패를 일으키는 것을 방지
error_reporting(E_ALL);
ini_set('display_errors', '0');
$host = 'localhost'; // 닷홈 DB 호스트
$db_name = 'jhbfarm'; // 닷홈 DB 명
$username = 'jhbfarm'; // 닷홈 DB 아이디
$password = '여기에_DB_비밀번호를_입력하세요'; // 닷홈 가입시 설정한 실제 DB 암호로 교체 필수
try {
$conn = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8mb4", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo json_encode(["success" => false, "error" => "데이터베이스 연결에 실패하였습니다: " . $e->getMessage()]);
exit;
}
$action = isset($_GET['action']) ? $_GET['action'] : '';
// 서버에 fileinfo 확장(mime_content_type)이 비활성화되어 있어도
// 이미지 업로드가 "서버 응답 오류"로 깨지지 않도록 단계적으로 형식을 판별하는 함수
function detectImageMime($tmpPath, $originalName) {
if (function_exists('mime_content_type')) {
$m = @mime_content_type($tmpPath);
if ($m) return $m;
}
if (function_exists('finfo_open')) {
$finfo = @finfo_open(FILEINFO_MIME_TYPE);
if ($finfo) {
$m = @finfo_file($finfo, $tmpPath);
finfo_close($finfo);
if ($m) return $m;
}
}
$info = @getimagesize($tmpPath);
if ($info && isset($info['mime'])) {
return $info['mime'];
}
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$extMap = ['jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'gif' => 'image/gif'];
return $extMap[$ext] ?? '';
}
// 데이터베이스 실행 도중 발생하는 예외 처리를 위한 글로벌 안전 장치
try {
// 1. 세션 확인
if ($action == 'get_session') {
if (isset($_SESSION['user'])) {
echo json_encode(["success" => true, "user" => $_SESSION['user']]);
} else {
echo json_encode(["success" => false, "error" => "로그인 세션이 존재하지 않습니다."]);
}
}
// 2. 로그인
elseif ($action == 'login') {
$user_id = $_POST['username'] ?? '';
$user_pw = $_POST['password'] ?? '';
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($user_pw, $user['password'])) {
$sessionUser = [
"id" => $user['id'],
"username" => $user['username'],
"name" => $user['name'],
"phone" => $user['phone'],
"role" => $user['role']
];
$_SESSION['user'] = $sessionUser;
echo json_encode(["success" => true, "user" => $sessionUser]);
} else {
echo json_encode(["success" => false, "error" => "아이디 또는 비밀번호가 일치하지 않습니다."]);
}
}
// 3. 회원가입
elseif ($action == 'register') {
$user_id = $_POST['username'] ?? '';
$user_pw = $_POST['password'] ?? '';
$name = $_POST['name'] ?? '';
$phone = $_POST['phone'] ?? '';
$stmt = $conn->prepare("SELECT id FROM users WHERE username = ?");
$stmt->execute([$user_id]);
if ($stmt->fetch()) {
echo json_encode(["success" => false, "error" => "이미 등록된 중복 ID입니다."]);
exit;
}
$hashed_pw = password_hash($user_pw, PASSWORD_BCRYPT);
$stmt = $conn->prepare("INSERT INTO users (username, password, name, phone, role) VALUES (?, ?, ?, ?, 'user')");
$result = $stmt->execute([$user_id, $hashed_pw, $name, $phone]);
if ($result) {
echo json_encode(["success" => true]);
} else {
echo json_encode(["success" => false, "error" => "가입 실패"]);
}
}
// 4. 로그아웃
elseif ($action == 'logout') {
session_destroy();
echo json_encode(["success" => true]);
}
// 4-1. 관리자 비밀번호 변경 (로그인된 관리자 본인만 가능)
elseif ($action == 'change_password') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
$cur_pw = $_POST['current_password'] ?? '';
$new_pw = $_POST['new_password'] ?? '';
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$user['id']]);
$dbUser = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$dbUser || !password_verify($cur_pw, $dbUser['password'])) {
echo json_encode(["success" => false, "error" => "현재 비밀번호가 일치하지 않습니다."]);
exit;
}
if (strlen($new_pw) < 4) {
echo json_encode(["success" => false, "error" => "새 비밀번호는 4자 이상이어야 합니다."]);
exit;
}
$hashed_new = password_hash($new_pw, PASSWORD_BCRYPT);
$stmt = $conn->prepare("UPDATE users SET password = ? WHERE id = ?");
$result = $stmt->execute([$hashed_new, $user['id']]);
if ($result) {
echo json_encode(["success" => true]);
} else {
echo json_encode(["success" => false, "error" => "비밀번호 변경 처리 중 오류가 발생했습니다."]);
}
}
// 5. 문의 제출 (회원가입 후 로그인한 회원만 신청/문의 가능)
elseif ($action == 'create_inquiry') {
if (!isset($_SESSION['user'])) {
echo json_encode(["success" => false, "error" => "온라인 신청 및 문의는 회원가입 후 로그인한 분만 이용하실 수 있습니다."]);
exit;
}
$name = $_POST['name'] ?? '';
$phone = $_POST['phone'] ?? '';
$item = $_POST['item'] ?? '';
$message = $_POST['message'] ?? '';
$user_id = $_SESSION['user']['id'];
$stmt = $conn->prepare("INSERT INTO inquiries (user_id, name, phone, item, message, status) VALUES (?, ?, ?, ?, ?, 'pending')");
$result = $stmt->execute([$user_id, $name, $phone, $item, $message]);
if ($result) {
echo json_encode(["success" => true]);
} else {
echo json_encode(["success" => false, "error" => "접수 오류"]);
}
}
// 6. 문의 조회
elseif ($action == 'get_inquiries') {
$user = $_SESSION['user'] ?? null;
if (!$user) {
echo json_encode(["success" => false, "error" => "권한이 없습니다."]);
exit;
}
if ($user['role'] == 'admin') {
$stmt = $conn->query("SELECT * FROM inquiries ORDER BY id DESC");
} else {
$stmt = $conn->prepare("SELECT * FROM inquiries WHERE user_id = ? ORDER BY id DESC");
$stmt->execute([$user['id']]);
}
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(["success" => true, "data" => $data]);
}
// 7. 문의 수정
elseif ($action == 'update_inquiry') {
$id = $_POST['id'] ?? '';
$item = $_POST['item'] ?? '';
$message = $_POST['message'] ?? '';
$user = $_SESSION['user'] ?? null;
if (!$user) {
echo json_encode(["success" => false, "error" => "비로그인 상태"]);
exit;
}
if ($user['role'] == 'admin') {
$name = $_POST['name'] ?? '';
$phone = $_POST['phone'] ?? '';
$status = $_POST['status'] ?? '';
$stmt = $conn->prepare("UPDATE inquiries SET name = ?, phone = ?, item = ?, message = ?, status = ? WHERE id = ?");
$stmt->execute([$name, $phone, $item, $message, $status, $id]);
} else {
$stmt = $conn->prepare("UPDATE inquiries SET item = ?, message = ? WHERE id = ? AND user_id = ? AND status = 'pending'");
$stmt->execute([$item, $message, $id, $user['id']]);
}
echo json_encode(["success" => true]);
}
// 8. 문의 삭제
elseif ($action == 'delete_inquiry') {
$id = $_POST['id'] ?? '';
$user = $_SESSION['user'] ?? null;
if (!$user) {
echo json_encode(["success" => false, "error" => "권한 상실"]);
exit;
}
if ($user['role'] == 'admin') {
$stmt = $conn->prepare("DELETE FROM inquiries WHERE id = ?");
$stmt->execute([$id]);
} else {
$stmt = $conn->prepare("DELETE FROM inquiries WHERE id = ? AND user_id = ? AND status = 'pending'");
$stmt->execute([$id, $user['id']]);
}
echo json_encode(["success" => true]);
}
// 9. 회원 목록 조회 (관리자 전용)
elseif ($action == 'get_users') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
$stmt = $conn->query("SELECT id, username, name, phone, role, created_at FROM users ORDER BY id ASC");
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(["success" => true, "data" => $data]);
}
// 10. 회원 권한 변경 (관리자 전용, 본인 제외)
elseif ($action == 'update_user_role') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
$targetId = $_POST['id'] ?? '';
$newRole = $_POST['role'] ?? '';
if (!in_array($newRole, ['user', 'admin'], true)) {
echo json_encode(["success" => false, "error" => "유효하지 않은 권한 값입니다."]);
exit;
}
if ((int)$targetId === (int)$user['id']) {
echo json_encode(["success" => false, "error" => "본인 계정의 권한은 변경할 수 없습니다."]);
exit;
}
$stmt = $conn->prepare("UPDATE users SET role = ? WHERE id = ?");
$result = $stmt->execute([$newRole, $targetId]);
echo json_encode(["success" => (bool)$result]);
}
// 11. 회원 삭제 (관리자 전용, 본인 제외)
elseif ($action == 'delete_user') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
$targetId = $_POST['id'] ?? '';
if ((int)$targetId === (int)$user['id']) {
echo json_encode(["success" => false, "error" => "본인 계정은 삭제할 수 없습니다."]);
exit;
}
$stmt = $conn->prepare("DELETE FROM users WHERE id = ?");
$result = $stmt->execute([$targetId]);
echo json_encode(["success" => (bool)$result]);
}
// 12. 사이트 콘텐츠 조회 (농장 이야기 / 이달의 농산물, 누구나 조회 가능 - 공개 페이지 렌더링용)
elseif ($action == 'get_site_content') {
$stmt = $conn->query("SELECT content_key, content_value FROM site_content");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$result = [];
foreach ($rows as $row) {
$result[$row['content_key']] = $row['content_value'];
}
echo json_encode(["success" => true, "data" => $result]);
}
// 13. 사이트 콘텐츠 저장 (관리자 전용 - 농장 이야기 / 이달의 농산물 등록,수정,삭제 모두 이 액션으로 통합 저장)
elseif ($action == 'set_site_content') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
$key = $_POST['key'] ?? '';
$value = $_POST['value'] ?? '';
if (!in_array($key, ['about', 'products', 'hero', 'logo', 'testimonials'], true)) {
echo json_encode(["success" => false, "error" => "유효하지 않은 콘텐츠 키입니다."]);
exit;
}
$stmt = $conn->prepare("INSERT INTO site_content (content_key, content_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE content_value = VALUES(content_value)");
$result = $stmt->execute([$key, $value]);
echo json_encode(["success" => (bool)$result]);
}
// 13-1. 회원 후기 등록 (관리자가 아니어도 로그인한 일반 회원이면 누구나 후기 작성 가능)
elseif ($action == 'add_testimonial') {
$user = $_SESSION['user'] ?? null;
if (!$user) {
echo json_encode(["success" => false, "error" => "후기 등록은 회원가입 후 로그인하신 분만 이용하실 수 있습니다."]);
exit;
}
$quote = trim($_POST['quote'] ?? '');
if ($quote === '') {
echo json_encode(["success" => false, "error" => "후기 내용을 입력해 주세요."]);
exit;
}
if (mb_strlen($quote) > 200) {
$quote = mb_substr($quote, 0, 200);
}
$stmt = $conn->prepare("SELECT content_value FROM site_content WHERE content_key = 'testimonials'");
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$testimonials = $row ? json_decode($row['content_value'], true) : null;
if (!is_array($testimonials)) {
$testimonials = ["eyebrow" => "먼저 받아보신 분들", "title" => "소개와 이웃의 신뢰로 자라온 농원", "items" => []];
}
if (!isset($testimonials['items']) || !is_array($testimonials['items'])) {
$testimonials['items'] = [];
}
$nextId = 1;
foreach ($testimonials['items'] as $it) {
if (isset($it['id']) && $it['id'] >= $nextId) {
$nextId = $it['id'] + 1;
}
}
$testimonials['items'][] = [
"id" => $nextId,
"quote" => $quote,
"who" => "— " . $user['name'] . "님"
];
$newValue = json_encode($testimonials, JSON_UNESCAPED_UNICODE);
$stmt = $conn->prepare("INSERT INTO site_content (content_key, content_value) VALUES ('testimonials', ?) ON DUPLICATE KEY UPDATE content_value = VALUES(content_value)");
$result = $stmt->execute([$newValue]);
echo json_encode(["success" => (bool)$result]);
}
// 14. 이달의 농산물 이미지 업로드 (관리자 전용)
elseif ($action == 'upload_product_image') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(["success" => false, "error" => "이미지 파일을 받지 못했습니다."]);
exit;
}
$file = $_FILES['image'];
$maxBytes = 3 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
echo json_encode(["success" => false, "error" => "이미지 용량은 3MB 이하만 업로드 가능합니다."]);
exit;
}
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'];
$mime = detectImageMime($file['tmp_name'], $file['name']);
if (!isset($allowed[$mime])) {
echo json_encode(["success" => false, "error" => "jpg, png, webp, gif 형식의 이미지만 업로드 가능합니다."]);
exit;
}
$uploadDir = __DIR__ . '/uploads/products/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filename = uniqid('product_', true) . '.' . $allowed[$mime];
$targetPath = $uploadDir . $filename;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
echo json_encode(["success" => true, "url" => "uploads/products/" . $filename]);
} else {
echo json_encode(["success" => false, "error" => "이미지 저장에 실패했습니다. uploads 폴더 쓰기 권한을 확인해 주세요."]);
}
}
// 15. 농장 이야기(농장 전경) 이미지 업로드 (관리자 전용)
elseif ($action == 'upload_about_image') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(["success" => false, "error" => "이미지 파일을 받지 못했습니다."]);
exit;
}
$file = $_FILES['image'];
$maxBytes = 3 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
echo json_encode(["success" => false, "error" => "이미지 용량은 3MB 이하만 업로드 가능합니다."]);
exit;
}
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'];
$mime = detectImageMime($file['tmp_name'], $file['name']);
if (!isset($allowed[$mime])) {
echo json_encode(["success" => false, "error" => "jpg, png, webp, gif 형식의 이미지만 업로드 가능합니다."]);
exit;
}
$uploadDir = __DIR__ . '/uploads/about/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filename = uniqid('about_', true) . '.' . $allowed[$mime];
$targetPath = $uploadDir . $filename;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
echo json_encode(["success" => true, "url" => "uploads/about/" . $filename]);
} else {
echo json_encode(["success" => false, "error" => "이미지 저장에 실패했습니다. uploads 폴더 쓰기 권한을 확인해 주세요."]);
}
}
// 16. 메인 로고 이미지 업로드 (관리자 전용)
elseif ($action == 'upload_logo_image') {
$user = $_SESSION['user'] ?? null;
if (!$user || $user['role'] !== 'admin') {
echo json_encode(["success" => false, "error" => "관리자 권한이 없습니다."]);
exit;
}
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(["success" => false, "error" => "이미지 파일을 받지 못했습니다."]);
exit;
}
$file = $_FILES['image'];
$maxBytes = 3 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
echo json_encode(["success" => false, "error" => "이미지 용량은 3MB 이하만 업로드 가능합니다."]);
exit;
}
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'];
$mime = detectImageMime($file['tmp_name'], $file['name']);
if (!isset($allowed[$mime])) {
echo json_encode(["success" => false, "error" => "jpg, png, webp, gif 형식의 이미지만 업로드 가능합니다."]);
exit;
}
$uploadDir = __DIR__ . '/uploads/logo/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filename = uniqid('logo_', true) . '.' . $allowed[$mime];
$targetPath = $uploadDir . $filename;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
echo json_encode(["success" => true, "url" => "uploads/logo/" . $filename]);
} else {
echo json_encode(["success" => false, "error" => "이미지 저장에 실패했습니다. uploads 폴더 쓰기 권한을 확인해 주세요."]);
}
}
} catch (PDOException $ex) {
// 테이블이 없거나 데이터베이스 쿼리 오류 발생 시 친절하게 안내 전송
echo json_encode([
"success" => false,
"error" => "데이터베이스 오류가 발생했습니다. phpMyAdmin에서 schema.sql을 실행하셨는지 확인해 주세요! 상세 내용: " . $ex->getMessage()
]);
}
?>
브라우저가 닷홈(Dothome) 서버로 요청을 보냈으나, 서버가 정상적인 JSON 데이터 대신 **HTML 텍스트**를 응답했습니다. 아래의 체크리스트를 확인해 주세요.
가장 흔한 에러(404 Not Found)입니다. 관리자 대시보드 내의 "배포" 탭에서 [api.php] 파일을 다운로드 한 뒤, 알조 FTP 또는 파일질라를 이용해 /hosting/jhbfarm/html/api.php 경로로 꼭 업로드해 주세요.
다운로드받은 `api.php` 파일 15번째 줄의 $password = '여기에_DB_비밀번호를_입력하세요'; 부분을 고객님의 실제 닷홈 가입 시 지정한 DB 비밀번호로 수정하여 업로드해야 정상 연동됩니다.
jhbfarm.dothome.co.kr/myadmin에 접속하신 후, 좌측에서 jhbfarm 데이터베이스 이름을 누르고 상단 SQL 탭에서 [schema.sql]의 쿼리 내용을 전체 복사하여 실행하셔야 테이블이 생성됩니다.