=== SOHO SEO ===
Contributors: luxsnap
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.0.27
License: GPLv2 or later

An SEO plugin focused on technical/structural factors affecting search visibility, not traditional keyword-density scoring.

== Description ==

(Note for reviewers: this description, Installation, External services and FAQ sections are written in English as requested. The plugin's admin screens and Changelog remain in Korean, since this plugin's users are Korean-language site owners — screen text is filtered through WordPress's own translation loading, and English translations can be added later via translate.wordpress.org.)

SOHO SEO checks and helps fix the technical/structural elements that actually affect search visibility (particularly Google Discover eligibility) rather than traditional keyword-density-style SEO scoring, which is deliberately not included. It also covers sitemap management, noindex management, 301/302 redirects, and internal/external link recommendations, plus a "human-likeness" post-quality check and internal/external link management, all in one plugin.

1. Applied automatically (active as soon as you install)
   - Forces max-image-preview:large in the robots meta tag (a requirement for large-image cards in Google Discover)
   - Automatically outputs Article structured data (JSON-LD) on every post (title/image/author/publish date/modified date/publisher)

2. Real-time checklist (post editor sidebar, refreshes automatically on save)
   - Featured image: at least 1200px wide, at least 300,000 total pixels, close to a 16:9 ratio (Google's official image requirements)
   - Whether Article structured data outputs correctly (recommended, not required by Google for Discover)
   - Clickbait/exaggeration detection in the title
   - Author display name (an E-E-A-T signal)
   - Mobile viewport meta tag (checked by loading the actual page when you click the button)
   - Whether a site icon (favicon) is set
   - Page experience: HTTPS (checked instantly) + PageSpeed Insights mobile performance score (checked automatically once an API key is registered).
     Since Google's February 2026 core update promoted page experience to an explicit Discover ranking factor, it carries the highest weight here.
   - A score out of 100 with per-item improvement advice (weighting: featured image 20 - page experience 30 - title quality 15 -
     structured data 10 - author display 10 - robots 5 - viewport 5 - favicon 5)

3. Bulk post-checklist dashboard (admin menu "SOHO SEO" > "Post SEO Checklist")
   - See scores for every published post at a glance
   - Re-check any single post with a "Re-check" button

4. Sitemaps (admin menu "SOHO SEO" > "Sitemap" — the on/off switch also lives on this screen)
   - Standard XML sitemap: /sitemap_index.xml (sub-sitemaps: /sitemap-posts.xml, /sitemap-pages.xml),
     including the featured image via <image:image> tags to help with image crawling.
   - Google News sitemap: /news-sitemap.xml (enabled on the same screen, includes only posts published in the last 48 hours).
   - WordPress core's default sitemap (wp-sitemap.xml) is disabled automatically to avoid duplication.
   - If you prefer another SEO plugin's sitemap, or don't need sitemaps at all, you can turn off just this
     plugin's sitemap generation from the "Sitemap" screen (the same on/off pattern used by Rank Math, Yoast, etc.).

5. Automatic internal/external link insertion — "Built-in" option (free, no external server involved;
   available from the post editor sidebar and the "Internal/External Links" menu)
   - Clicking the button extracts candidate keywords/phrases from the whole post using a local, rule-based
     method (filtering particles, endings and stop words). Internal and external link candidates are chosen
     with different criteria:
       - Internal link candidates: broad, generally-recognized terms such as place names, event names,
         countries/cities, or restaurant/lodging names (e.g. "a trip to Osaka, Japan" -> "Osaka").
       - External link candidates: specific, technical compound terms such as jargon, weapon-system or
         institution names (e.g. "hypersonic weapon system", "missile defense system").
     If you need more context-aware candidate suggestions, see the paid "Network extraction" option in item 8 below.
   - Internal links: for every checked word (not just one), it automatically finds and links to the most
     relevant other post on the site (based on title match and frequency of appearance in the body).
   - External links: for every checked word, it automatically finds the most relevant Korean Wikipedia
     (ko.wikipedia.org) article and links to it in a new tab (see "External services" below).
   - In both cases, only the first occurrence of the word in the post is linked, and the post is published
     immediately afterward.

6. Noindex management (admin menu "SOHO SEO" > "Noindex Management", free)
   - Automatically adds <meta name="robots" content="noindex"> only to archive-type pages that don't need
     search visibility (date/monthly archives, author archives, tag archives, on-site search results, and
     category archives you select yourself).
   - Uses meta noindex rather than blocking crawling via robots.txt — blocking crawling would prevent Google
     from re-crawling and could leave already-indexed pages stuck in the index.
   - Individual posts and pages are never affected by any combination of this feature's settings (there is an
     unconditional safeguard at the top of the code for this).
   - Every option defaults to off; nothing is applied unless an administrator explicitly checks it.

7. Redirects (admin menu "SOHO SEO" > "Redirects")
   - Integrates the 301/302/410 redirect management and 404 logging that used to be a separate plugin
     ("LUX Redirect Manager") into this plugin.
   - Supports exact-match and wildcard (*) patterns, hit counts, automatic 404 log collection, CSV export,
     and a "Create redirect" button that goes straight from a 404 log entry to the redirect form.
   - If you were previously using "LUX Redirect Manager" separately, the database table names are kept
     identical so your data carries over as-is. Since the functionality is now duplicated, we recommend
     deactivating the separate plugin.

8. SOHO Cloud — a set of features that communicate with the soho-api.luxdigest.com server. Features 1-7 above
   keep working for free regardless of whether this integration is used. This server address is fixed to
   this plugin, shown read-only on the settings screen for transparency, and cannot be changed by the user.
   - Tag cleanup (admin menu "SOHO SEO" > "Tag Cleanup"): automatically classifies every tag into orphaned
     (0 posts), thin (1 post), normal, or protected, shown as a sortable/filterable/searchable table, with
     single or bulk deletion. Tags you mark "protected" are always excluded from statistics and deletion.
     Viewing, protecting/unprotecting and deleting are all free with no limit, and processed entirely on
     this site (no external server is contacted for this feature).
   - Credits & plans (admin menu "SOHO SEO" > "Credits & Plans"): shows your current credit balance,
     first-signup trial credits, the monthly subscription plan, and top-up pricing, and lets you subscribe
     or top up credits via the active payment processor (see "External services" below).
   - Human-likeness post check (admin menu "SOHO SEO" > "Human-likeness Post Check", and the post editor
     sidebar): from a paginated, category-filterable list of published posts, clicking "Check" expands an
     accordion under that post showing a score out of 100, a detailed and humble overall review, and the
     reasoning behind each sub-score. It never rewrites sentences or publishes anything on your behalf — it
     is a pure diagnostic showing "how human this post reads." This is a paid, credit-based feature processed
     by the SOHO Intelligence process on the SOHO Cloud server (see "External services" below; no API key of
     this site or any user is ever used).
   - Internal/external link management (admin menu "SOHO SEO" > "Internal/External Links", and the post
     editor sidebar): from the same paginated, category-filterable post list, choose one of three ways to
     add links:
       - Built-in (free): extracts candidates locally on this site using word-based rules, without
         contacting any server. The candidate list may be somewhat less refined than the network-based option.
       - Network extraction - manual (paid): the SOHO Cloud server analyzes the actual post's context to
         recommend candidates (processed on the SOHO Cloud server as above). Not perfect, but
         more thorough than the built-in option. You review and check internal/external candidates in a
         popup before they're applied; publish status is left unchanged.
       - Network extraction - automatic (paid): the same server analysis is applied straight to the post
         and published with one click, with no manual review step.
     Both the manual and automatic modes let you restrict the scope to "internal only / external only / both".
   - Tag cleanup (in full) and the built-in link option work immediately with no account required; only the
     credit-based features (human-likeness check, network-extraction link management) talk to the SOHO Cloud
     server.
   - If you try to run a check/suggestion without enough credit, the screen immediately offers an "Apply for
     trial credit" button so you can request credit and retry right there.
   - Site error check (admin menu "SOHO SEO" > "Site Error Check (Key Search Engine Items)", paid, credit-based): once enabled with an
     alert email address, the SOHO Cloud server itself checks this site's robots.txt and every sitemap this
     plugin provides (sitemap index, posts, pages, news - whichever are actually turned on) once a day at
     03:17 Korea time, independent of this site's own traffic or WP-Cron, so it still runs reliably on
     low-traffic sites. It sends an email only when something looks wrong (robots.txt blocking all search
     engines, a sitemap disappearing or its item count dropping sharply, its structure changing, etc.). The
     same screen also shows, for free with no credit cost, a live preview of the current robots.txt content
     and the status of every sitemap in use, a "Check now" button that runs the identical paid check on
     demand, and a "Send test email" button to confirm the alert address works. Each check (scheduled or
     manual) consumes a fixed number of credits; nothing is checked and no credit is charged unless this is
     explicitly turned on.

== Installation ==

1. Upload this zip file via WordPress Admin > Plugins > Add New > Upload Plugin.
2. Activate the plugin. (Sitemap URL rewrite rules are registered automatically on activation.)
3. Under the admin menu "SOHO SEO" > "Settings" (at the bottom of the left-hand menu), set your publisher
   name and logo.
4. (Optional) On the same Settings screen, register a free PageSpeed Insights API key to have the page
   experience (loading speed) item checked automatically. Get a free key at
   developers.google.com/speed/docs/insights/v5/get-started.
5. (Optional) On the "SOHO SEO" > "Sitemap" screen, turn general/news sitemaps on or off (the general sitemap
   기본값 켜짐) 뉴스 발행처명·언어 코드를 설정할 수 있습니다. 다른 SEO 플러그인의
   사이트맵을 대신 쓰신다면 일반 사이트맵을 꺼두세요.
6. (Optional) On "SOHO SEO" > "Noindex Management", pick the archive-type pages that don't need search
   visibility and apply noindex. Posts and pages are never affected by any setting on this screen.
7. If sitemap URLs return 404, open Settings > Permalinks once and click "Save Changes" to refresh the
   rewrite rules.
8. Submit your sitemap to Google Search Console.
9. (Optional) The human-likeness post check, the "network extraction" option in internal/external link
   management, and subscription/top-up payments communicate with the SOHO Cloud server
   (soho-api.luxdigest.com, a fixed address shown read-only on the settings screen). Tag cleanup (in full) and
   the "Built-in" link option work immediately with no account required.

== External services ==

This plugin's free features (robots meta tag, structured data, real-time checklist, sitemaps, the built-in
internal-link option, noindex management, redirects) work entirely on this site with no external service
involved. Only the features below communicate with the external services listed.

* SOHO SEO Cloud server (soho-api.luxdigest.com, operated by LUX Digest, the developer of this plugin)
  - Used by the human-likeness post check, network-extraction internal/external link management, credit/plan
    lookups, and payment (Paddle or Lemon Squeezy integration). Tag cleanup is handled entirely locally on this site and never
    contacts this server.
  - This site's domain and the title/body of the post being analyzed are sent. For link recommendations, the
    server itself reads the titles and URLs of this site's published posts from this site's public REST API
    (/wp-json/wp/v2/posts) to build the internal-link candidate list, instead of this site sending that list.
    Long-running work is accepted as a job and processed on the server; the administrator's browser then asks
    the server for the result directly, so this site does not wait for server-side processing. If site health monitoring is
    enabled, this site's domain and the alert email address you enter are also sent, so the server can run the
    daily robots.txt/sitemap check and email that address when it finds a problem.
  - Site authentication: the first time this site talks to the server, the plugin creates a random token and asks
    the server to register this domain. The server then requests https://this-site/?sohose_verify=<random value>
    once, and the plugin answers with an HMAC of that value (the token itself is never sent back), which proves the
    request really came from this site. The server then issues a site-specific secret key, stored in this site's
    database, and every later request is signed with it so no one else can use this site's credits or settings.
  - This server address is fixed and cannot be changed by the user.
  - Terms of Service / Privacy Policy: https://sohoseo.luxdigest.com

* Anthropic Claude API (anthropic.com) / OpenAI API (openai.com)
  - Called by the SOHO SEO Cloud server above, using the server's own account, to process the
    human-likeness post check and network-extraction link recommendations. The Claude API is used first,
    automatically falling back to the OpenAI API only if the request fails or is unavailable. Neither this
    site nor the user registers a separate API key; in both cases the request content (the title/body of the
    post being analyzed) passes through the SOHO SEO Cloud server.
  - Terms of use: https://www.anthropic.com/legal/consumer-terms /
    https://openai.com/policies/row-terms-of-use/

* Korean Wikipedia (ko.wikipedia.org, operated by the Wikimedia Foundation)
  - Used by the external-link recommendation feature to check whether a matching article exists for a
    candidate word and to retrieve that article's URL.
  - Terms of use: https://foundation.wikimedia.org/wiki/Policy:Terms_of_Use

* Google PageSpeed Insights API (used only if the user registers their own API key on the Settings screen;
  optional)
  - Used in the post editor to check page loading speed. The request is sent directly from the administrator's
    browser to Google (this site's server does not call it), together with the post's public URL and your key.
  - Terms of use: https://developers.google.com/speed/docs/insights/v5/get-started

* Paddle (paddle.com) or Lemon Squeezy (lemonsqueezy.com) — payment processor. The SOHO SEO Cloud server is
  configured to use exactly one of the two at a time; whichever is active is what a given installation uses.
  - Clicking the subscribe/top-up payment button on the "Credits & Plans" screen opens the active processor's
    own checkout (either Paddle.js, loaded from cdn.paddle.com, or a Lemon Squeezy-hosted checkout page opened
    in a new tab). Card and other payment details are never sent to or stored by this plugin, this site, or
    the SOHO SEO Cloud server — the payment processor handles them directly. Only the payment result is sent
    by the processor to the SOHO SEO Cloud server so credits/plans can be updated.
  - Terms of use: https://www.paddle.com/legal/terms and https://www.lemonsqueezy.com/terms

== Frequently Asked Questions ==

= Does installing this plugin alone get me into Google Discover? =

No. Google Discover decides inclusion algorithmically, and installing any single plugin can't guarantee it.
This plugin helps you fully meet the technical/structural requirements Google officially documents (image
size, structured data, mobile-friendliness, trust signals, etc.). Content quality, freshness, and overall
site experience (avoiding excessive ads, loading speed, etc.) also matter and should be managed alongside it.

= Do I have to use the paid features? Can I use this plugin entirely for free? =

The robots meta tag, structured data, real-time checklist, sitemaps, noindex management, redirects, tag
cleanup (in full), and the "Built-in" option of internal/external link management are all free and work
immediately with no account or server communication required. Only the human-likeness post check and the
"network extraction" option of internal/external link management are paid, credit-based features.

= How do I pay for the paid features? Is my card information stored on this site? =

Subscriptions and credit top-ups are handled through the payment processor's checkout (Paddle or Lemon
Squeezy, depending on server configuration) on the "Credits & Plans" screen. Card information is handled
directly by that processor and is never sent to or stored by this plugin, this site, or the SOHO SEO Cloud
server.

= How are external links connected? Do I need to type in a URL myself? =

No. The free "Built-in" option uses the Korean Wikipedia (ko.wikipedia.org) OpenSearch API, and the paid
"network extraction" option has the SOHO Cloud server automatically find a URL (an official homepage or a
Wikipedia article) for key terms it extracts from the post through its server-side analysis. Neither method requires you to type in
a URL yourself.

== Changelog ==

= 1.0.27 =
* 감성형 포스팅 체크: "미점검 글" / "점검 완료 글" 두 탭으로 분리. 점검이 끝난 글은 점검 완료 탭으로 옮겨집니다.
* 점검 결과(점수·총평·점수 근거·점검일·분량·차감 크레딧)를 글마다 최신 1건 저장. 새로고침해도 결과가 사라지지 않고, "상세보기"로 언제든 다시 볼 수 있습니다(크레딧 차감 없음).
* 재검사 버튼 추가. 점검 이후 본문 문장이 바뀌면 "점검 후 수정됨"으로 표시(링크 삽입·서식 변경에는 반응하지 않음). 바뀐 내용이 없을 때는 재검사 전에 한 번 확인합니다.
* 점검 중 창을 닫아도 30분 안에 목록을 다시 열면 결과를 받아와 자동 저장.
* 같은 글을 동시에 두 번 점검(크레딧 이중 차감)하지 않도록 잠금.
* 글 편집 화면에서 한 점검도 같은 기록으로 저장.
* 점검 결과는 플러그인을 삭제해도 남습니다(재설치 시 그대로 표시).

= 1.0.26 =
* 크레딧 고정 단가·누진제 도입(SOHO 서버와 함께 적용): 감성형 포스팅 체크는 한글 단어(띄어쓰기) 기준 1,000단어까지 30크레딧, 1,001~3,000단어 구간 1,000단어당 +20, 3,001~6,000단어 +30, 6,001단어 이상 +45. 내부링크 4개까지 20·외부링크 8개까지 40크레딧, 그 이상은 4개 단위 누진(내부 +10·+15·+20… / 외부 +20·+30·+40…), 내부+외부는 합산. 사이트 에러체크 1일 10크레딧.
* 1,000단어를 넘는 글은 감성형 포스팅 체크 실행 전에 단어 수와 차감 크레딧을 먼저 안내하고 확인을 받습니다. 잔액이 모자라면 작업을 걸지 않고 필요한 크레딧을 알려줍니다.
* "내부/외부링크 관리" 화면에 글 1개당 최대 링크 수 선택(개수별 크레딧 표시) 추가.
* "크레딧 충전" 메뉴를 "크레딧 사용현황"으로 변경하고 기능별 단가표·최근 사용 내역을 표시합니다.
* 체험 크레딧 사용 중인 사이트에 쓸 수 있는 기능과 남은 크레딧을 안내하고, 사이트 에러체크가 켜져 있으면 자동 차감으로 소진되는 날(D-day)을 SOHO SEO 화면 상단과 크레딧 사용현황 화면에 표시합니다.

= 1.0.25 =
* 체험 크레딧 자동 지급: 사이트 인증이 끝나는 즉시, 그리고 유료 기능을 처음 쓰기 직전에 체험 크레딧을 자동으로 신청합니다(도메인당 1회). 새로 설치한 무료 사용자가 "크레딧 충전"만 안내받던 문제 수정.
* 편집기(감성형 포스팅 체크·내부/외부링크 관리)와 스페셜발행의 "크레딧 부족" 안내에 "체험 크레딧 받기" 버튼 추가.
* 체험 크레딧 신청 결과를 실제 지급 여부(granted)로 판단: 이미 받은 도메인인데도 "지급되었습니다"라고 표시하고 재시도하던 문제 수정, 잔여 크레딧을 함께 안내.
* "체험 크레딧 받기"를 누를 때 사이트 인증이 끝나지 않았으면 먼저 인증을 진행하고 안내합니다.
* 이미 체험 크레딧을 받은 사이트에 "먼저 체험 크레딧을 신청해주세요"를 다시 보여주던 문제 수정 — 잔액 부족 안내와 충전 화면 링크만 표시합니다.

= 1.0.24 =
* 사이트맵 설정 화면과 실제 파일의 대응 관계 명시: 체크박스마다 만들어지는 파일명(sitemap-posts.xml, sitemap-pages.xml, sitemap-categories.xml …)과 홈 주소가 sitemap-pages.xml에 함께 들어간다는 점을 표시. "분류(아카이브)" → "카테고리·태그 사이트맵"으로 명칭 정리.
* 설정에서 끈 사이트맵 주소가 sitemap_index.xml로 301 이동되던 구조 수정 → 404로 응답.

= 1.0.23 =
* (사이트 에러체크) 자동 점검 시각을 매일 00:05에서 03:17(한국시간)로 옮겼습니다. 자정 전후 호스팅 작업과 겹쳐 실제로는 정상인데 이상으로 판정되던 문제를 줄입니다.
* (사이트 에러체크) 응답 대기 시간을 10초에서 20초로 늘리고, 일시적인 실패(응답 없음·404·서버 오류)는 1분, 2분 간격으로 최대 3번까지 다시 확인한 뒤에만 이상으로 판정합니다.
* (사이트 에러체크) 뉴스 사이트맵은 최근 48시간 글만 담아 개수가 줄어드는 게 정상이므로, "항목 수 급감" 판정에서 제외했습니다(형식·구조 검사는 그대로 합니다).

= 1.0.22 =
* (문구) 화면에 보이는 모든 안내 문구를 SOHO 인텔리전스 프로세스·SOHO 서버 기준 표현으로 통일했습니다(예: "SOHO 인텔리전스 프로세스가 글을 분석 중입니다").
* (스페셜발행) 안내 문구를 실제 동작(기다리는 동안 다른 화면으로 가도 되고, 돌아오면 이어서 받아 발행)에 맞게 고쳤습니다.

= 1.0.21 =
* (버그 수정) 글 편집 화면 사이드바의 "링크 추천"이 1.0.17부터 항상 "추천할 링크가 없습니다"로 끝나던 문제를 고쳤습니다(서버가 먼저 돌려주는 작업 번호를 처리하지 못하던 문제).
* (스페셜발행) 조사·집필을 SOHO 서버가 전부 맡고, 워드프레스는 접수와 발행만 합니다. 예전처럼 워드프레스가 최대 5분 가까이 붙잡혀 있지 않습니다. 기다리는 동안 다른 화면으로 가도 되며, 스페셜발행 화면으로 돌아오면 결과를 이어서 받아 발행합니다. 버튼을 두 번 눌러도 크레딧이 두 번 차감되거나 글이 두 번 발행되지 않습니다.
* (모든 클라우드 기능) 감성형 포스팅 체크, 링크 추천(사이드바·링크 관리 화면), 자동 적용+발행, 스페셜발행에서 결과를 기다리는 동안 경과 시간, 예상 소요 시간, 이 화면에서 할 일을 함께 보여줍니다. 3분이 넘으면 "서버는 계속 처리하고 있습니다" 안내로 바뀝니다.
* (재점검) 모바일 화면·PageSpeed 측정을 포함한 재점검 버튼에 경과 시간을 표시합니다.

= 1.0.20 =
* (자동 발행) 서버에 맡긴 작업이 있으면 "처리 현황" 표 맨 위에 "서버에서 처리 중" 줄과 경과 시간이 실시간으로 표시됩니다. 매시간 자동 실행으로 맡긴 작업도 똑같이 보이며, 결과가 오면 새로고침 없이 그 자리에서 완료/다시 처리 예정으로 바뀝니다.
* (자동 발행) "지금 테스트 실행" 결과를 버튼 옆 작은 글씨 대신 눈에 띄는 안내 상자로 보여줍니다. 처리 중에는 "다른 일을 하셔도 됩니다" 안내가 함께 나오고, 처리 중에는 버튼을 다시 누를 수 없습니다.
* (자동 발행) 사이트 인증이 끝나기 전에 테스트를 누르면 실패로 기록하지 않고, 인증이 끝나는 즉시 테스트를 이어서 합니다. 매시간 자동 실행도 인증 대기 중에는 기록 없이 다음 실행에서 처리합니다.
* (자동 발행) 표의 "적용 개수/오류" 칸을 "결과" 칸 하나로 합쳐 완료 / 서버에서 처리 중 / 다시 처리 예정 / 다시 처리됨을 한눈에 구분할 수 있게 했습니다. 기술적인 오류 문구는 알아듣기 쉬운 말로 바꿨습니다.
* (자동 발행) "다음 자동 실행 예정" 시각이 사이트 시간대(예: 한국 시간)로 표시되지 않던 문제를 고쳤습니다.

= 1.0.19 =
* (사이트 인증) 서버가 아직 새 버전이 아닐 때 인증을 6시간 동안 다시 시도하지 않던 문제를 고쳤습니다. 이제 어떤 이유로 실패해도 2~10분 안에 자동으로 다시 시도하며, 1.0.18에서 대기 중이던 사이트도 업데이트하는 즉시 인증을 시작합니다.
* (사이트 인증) 인증이 끝나기 전에는 SOHO SEO 화면과 플러그인 목록 화면에 진행 상황 안내창이 항상 보입니다. 진행 중에는 "다른 일을 하셔도 됩니다" 안내가, 실패하면 사유와 "지금 다시 인증" 버튼이 나오고, 완료되면 그 자리에서 "인증 완료"로 바뀝니다. 크레딧 충전 화면에는 인증 완료 여부가 항상 표시됩니다.
* (사이트 인증) 서버에 이 사이트의 키가 없어진 경우(서버 데이터 초기화 등)에도 자동으로 다시 인증합니다.
* (사이트 인증) 인증이 끝나기 전에 클라우드 기능을 누르면 "서버로부터 수신 결과를 받는 과정에서 얼마간의 시간이 필요합니다. 다른 일을 하셔도 됩니다"라는 안내가 나옵니다.

= 1.0.18 =
* (보안 — 사이트 인증) SOHO 서버가 요청에 적힌 도메인을 그대로 믿던 구조를 바꿨습니다. 이제 사이트마다
  "도메인 소유 확인"을 거쳐 전용 비밀키를 발급받고, 서버로 가는 모든 요청에 그 키로 서명합니다(시각·일회용 값 포함,
  재전송 차단). 다른 사람이 이 사이트 도메인을 적어 보내도 크레딧을 쓰거나 설정을 바꿀 수 없습니다.
  - 자동 진행: 업데이트 후 관리자 화면을 열거나 클라우드 기능을 쓰면 1~2분 안에 저절로 인증됩니다.
  - 사이트 주소를 옮기거나 키가 어긋나면 자동으로 다시 인증합니다.
  - 보안 플러그인·방화벽이 인증 확인 요청(?sohose_verify=)을 막으면 SOHO SEO 화면에 안내가 표시됩니다.

= 1.0.17 =
* (속도 — 워드프레스 부담 제거) 오래 걸리는 작업은 SOHO 클라우드 서버에 "접수"만 하고 워드프레스는 즉시 빠지도록 바꿨습니다.
  - 링크 추천(수동·자동): 작업 접수 후 1초 안에 응답. 결과 확인은 관리자 브라우저가 서버에 직접 묻습니다
    (예전: 워드프레스가 최대 200초 동안 응답을 기다림). 내부링크 후보 글 목록도 서버가 사이트의 공개 REST API에서
    직접 모읍니다(예전: 매번 워드프레스가 글 200개 목록을 만들어 보냄). REST API가 막힌 사이트만 예전처럼 목록을 보냅니다.
  - 자동 발행(클라우드 엔진): 매시간 작업을 접수만 하고, 2분 뒤 짧은 확인으로 결과를 받아 적용합니다.
    처리하는 사이 글이 수정되면 결과를 덮어쓰지 않습니다.
  - 감성형 포스팅 체크: 결과 확인(몇 초 간격)을 관리자 브라우저가 서버에 직접 합니다(워드프레스 요청 없음).
  - SEO 체크리스트의 모바일 뷰포트·PageSpeed 확인, 사이트맵 자가 진단의 주소 불러오기를 관리자 브라우저가 합니다
    (예전: 워드프레스 서버가 자기 자신을 다시 불러와 최대 8~30초 대기).
  - 사이트 에러체크: 사이트맵 목록 동기화를 매시간 → 하루 1회, 그리고 목록이 바뀌었을 때만 보냅니다.
  - SOHO 서버 짧은 요청의 대기 시간을 6초로 줄이고, 서버 연결이 안 되면 5분간 다시 부르지 않습니다.
  - 크레딧 화면의 결제 설정 조회를 1시간 캐시합니다.
* (속도 — 방문자 요청) 리다이렉트 조회가 인덱스를 쓰지 못해 페이지마다 리다이렉트 표 전체를 훑던 문제를 고쳤습니다
  (판정 결과는 예전과 같음). 봇의 취약점 탐색(.php/.env 등)·정적 파일 404는 기록하지 않습니다.
* 예전 버전 SOHO 서버와도 그대로 동작합니다(작업 접수를 모르는 서버면 예전 방식으로 처리).

= 1.0.16 =
* (사이트맵 전면 개편) "SOHO SEO > 사이트맵"을 LUX Sitemap Generator와 같은 엔진·같은 화면으로
  교체. 현황·진단 / 일반 / 콘텐츠 / 제외 / 구글 뉴스 / IndexNow / 호환성 7개 탭.
  - 커스텀 글 유형, 카테고리·태그·커스텀 택소노미, 작성자 사이트맵, 홈 주소 포함,
    분할 개수 설정과 페이지별 정확한 lastmod, 뉴스 사이트맵 인덱스 포함·카테고리 제외.
  - IndexNow(네이버·빙 등) 즉시 색인 요청, 자가 진단(리다이렉트/HTML 응답/XML 오류/
    캐시된 옛 사본/빈 사이트맵/robots.txt/플러그인 충돌), 글별 "사이트맵에서 제외" 상자,
    SOHO 노인덱스 관리·Yoast·Rank Math·AIOSEO·SEOPress의 noindex 자동 제외,
    예전 주소(/sitemap.xml, /wp-sitemap.xml, /post-sitemap.xml 등) 301 이동, 기본형 고정 링크 지원.
  - 주소 형식은 그대로라 서치콘솔에 다시 제출할 필요 없음. 기존 사이트맵 설정(사용 여부,
    뉴스 발행처·언어, 제작사 표시)은 자동으로 이어받고, 같은 사이트에 LUX Sitemap Generator
    설정이 있으면 그 설정을 가져온다.
  - 같은 사이트에 LUX Sitemap Generator가 켜져 있으면 SOHO SEO 사이트맵은 자동으로 물러나
    한쪽만 응답한다(충돌 방지). 관리 화면에서 상태를 안내하고 설정을 가져올 수 있다.
  - 사이트 에러체크의 점검 대상 사이트맵 목록이 새 엔진의 실제 목록을 그대로 따른다.
* (구조화 데이터·사이트맵 공용 이미지 추출 보강) 숏코드/블록이 직접 출력한 내용이 페이지나
  사이트맵에 섞이지 않도록 차단, 지연 로딩(data-src) 이미지 인식, http/https 혼용 및 상대 경로
  이미지 인식, 렌더링 중 전역 $post 보정. 이미지 캐시 키를 _v3로 올려 한 번 다시 계산된다.
* (버그 수정) 플러그인 비활성화 시 사이트맵 주소 규칙이 남아 있던 문제 — 비활성화 후 첫
  요청에서 규칙을 새로 만들도록 변경.

= 1.0.15 =
* (성능/버그 수정 · 실서버에서 확인됨) 자동 발행(예약 실행) 기능이 켜져 있고 처리
  엔진이 기본값인 "로컬"일 때, 매시간 크론이 새 글의 외부링크 후보 단어를 찾으려고
  위키백과에 순서대로 동기 요청을 보내는데, 캐시 안 된 단어가 여러 개 겹치면(특히
  방금 발행된 새 글) 한 번의 실행이 PHP-FPM 워커 하나를 최대 1~2분 가까이 붙잡아둘
  수 있었다. 조기 종료 장치가 전혀 없었던 것이 원인으로, 워커 풀이 작은 호스팅에서는
  그 동안 다른 방문자 요청이 전부 밀려 사이트 전체가 느려지거나 버퍼링되는 것처럼
  보였다(플러그인을 비활성화하면 즉시 정상, 활성화하면 재현되는 것으로 확인). 이제
  이 루프에 20초 시간 예산을 두어, 예산을 넘어서면 그 시점부터는 아직 캐시가 없는
  단어에 대한 새 조회만 건너뛰고, 이미 캐시된 조회와 내부링크 매칭은 그대로 정상
  진행한다. "내부/외부링크 관리" 화면에서 관리자가 직접 누르는 "외부링크 적용"
  버튼도 같은 원인(체크한 단어가 많을 때)으로 오래 걸릴 수 있었던 부분을 동일하게
  고쳤다.

= 1.0.14 =
* (노인덱스 관리) 저장 시 항목 하나가 어떤 이유로 요청에 실려 오지 않으면 그 항목이
  조용히 "색인 유지"로 초기화되던 문제를 고쳤다. 이제 저장은 빈 기본값이 아니라
  현재 DB에 저장된 값에서 시작해서, 실제로 요청에 담겨 온 항목만 갱신하고 나머지는
  기존 값을 그대로 유지한다.

= 1.0.13 =
* (결제 연동) 크레딧 충전 화면의 결제 버튼 분기 로직을 "레몬스퀴지면 A, 아니면 무조건
  패들"로 짜여 있던 방식에서 "패들이면 A, 아니면 공용 결제창 흐름"으로 바꿨다. 예전
  방식은 새로운 결제대행사가 추가될 때마다(레몬스퀴지 → 이번 결제사) 조건이 하나도
  안 맞아 버튼을 눌러도 아무 반응이 없는 문제가 실제로 발생했었다 — 이제 결제대행사가
  무엇이든(패들 제외) 같은 공용 흐름(서버가 만들어준 결제창 URL을 새 탭으로 열기)을
  타므로 이 문제가 재발하지 않는다. 패들 전용 스크립트(Paddle.js)도 결제수단이 정확히
  패들일 때만 불러오도록 함께 정리했다.

= 1.0.12 =
* (버그 수정 · 서치콘솔에서 확인됨: "사이트맵을 읽을 수 있지만 오류가 있습니다 / urlset
  안에 url 태그가 없음") 글 사이트맵(sitemap-posts-N.xml)은 글마다 대표 이미지 외에
  본문 속 콘텐츠 이미지까지 뽑아내기 위해 본문을 do_blocks()/do_shortcode()로 렌더링한다.
  이 결과는 글마다 캐시해두지만, 캐시가 없는(또는 본문이 막 바뀐) 글이 한 페이지에
  몰려 있으면 한 요청에서 수십 건을 한꺼번에 새로 렌더링해야 해 시간이 오래 걸릴 수
  있었고, 그중 하나라도 처리 중 문제가 생기면(느린 블록/숏코드로 인한 실행시간 초과,
  또는 PHP 경고가 화면에 그대로 출력되는 호스팅 설정 등) 응답이 중간에서 끊겨
  </urlset> 마감 태그를 내보내지 못한 채 끝나버릴 수 있었다 — 서치콘솔이 신고한
  증상과 정확히 일치한다(문서가 끝까지 완성되지 못하면 구글 파서가 사실상 빈 문서로
  취급). 이번 버전에서는 사이트맵 생성 중 (1) 실행시간 제한을 해제하고 (2) PHP
  경고/알림이 화면(XML 스트림)에 출력되지 않도록 막았으며(에러 로그 기록에는 영향
  없음) (3) 글 하나의 이미지 추출·처리 중 오류가 나도 그 글만 건너뛰고(가능하면
  loc/lastmod만이라도 남기고) 나머지는 계속 진행해, 어떤 경우에도 사이트맵이
  </urlset>로 정상 마감되도록 했다.

= 1.0.11 =
* (버그 수정 · 근본 원인 해결) 다국어 사이트에서 글 사이트맵이 일부 언어의 글만 반영하는
  문제의 원인을 WP_Query 훅(pre_get_posts/posts_pre_query) 차단 방식으로 두 차례
  고쳤으나, 캐시(HTTP 캐시·서버 OPcache)를 전부 비운 뒤에도 결과가 바뀌지 않는 것이
  실서버에서 확인되어, WP_Query를 아예 거치지 않고 발행글 ID를 데이터베이스에서 직접
  조회하는 방식으로 바꿨습니다. 이제 어떤 플러그인이 어떤 방식으로 글 목록 조회에
  개입하든(pre_get_posts든 다른 지점이든) 영향을 받지 않고, 사이트맵이 실제 발행글
  전체(모든 언어 포함)를 정확히 반영합니다.

= 1.0.10 =
* (버그 수정 · 핵심 원인 추가 확인) 1.0.9의 "캐시 비우고 지금 다시 확인" 버튼을 눌러도
  사이트맵 수치가 그대로인 사례가 확인되어, LiteSpeed Cache 등 HTTP 응답 캐시보다 한
  단계 더 아래에 있는 PHP OPcache(서버가 .php 코드를 미리 컴파일해 재사용하는 캐시)까지
  확인 범위를 넓혔습니다. 호스팅 환경에 따라 이 캐시가 파일 수정 시각을 확인하지 않도록
  설정돼 있으면, 플러그인 코드를 새로 올려도 서버가 업로드 전 옛 코드를 계속 실행할 수
  있습니다. 이제 "캐시 비우고 지금 다시 확인" 버튼과 업데이트 시 자동 캐시 비우기 모두
  이 서버 단 캐시까지 함께 강제로 비웁니다.

= 1.0.9 =
* (신규) "사이트 에러체크" 화면의 사이트맵 미리보기에 "캐시 비우고 지금 다시 확인"
  버튼을 추가했습니다. 이 플러그인을 업데이트해도 사이트맵 수치가 즉시 바뀌지 않는
  경우, 원인이 이 플러그인의 코드가 아니라 서버/호스팅사 캐시(LiteSpeed Cache 등)에
  남아있는 예전 사본일 수 있습니다. 이 버튼을 누르면 관련 캐시를 전부 비운 뒤 곧바로
  다시 확인한 결과를 같은 화면에서 바로 보여줘, 플러그인을 다시 올리지 않고도 그
  자리에서 원인을 구분할 수 있습니다.
* (강화) 사이트맵 캐시 비우기가 개별 주소 단위 삭제(URL이 캐시된 형태와 정확히
  일치하지 않으면 지워지지 않을 수 있음)에만 의존하지 않도록, 안전망으로 캐시 전체
  비우기도 함께 호출하도록 보강했습니다(LiteSpeed Cache/W3 Total Cache/WP Rocket).
* (신규) 사이트맵 XML 응답에 생성 시각을 담은 주석을 추가했습니다. 검색엔진 색인에는
  영향이 없으며, 사이트맵 주소를 새로고침했을 때 이 시각이 그대로인지 바뀌는지로
  캐시 문제인지 아닌지를 눈으로 바로 확인할 수 있습니다.

= 1.0.8 =
* (버그 수정) 1.0.7의 다국어 사이트맵 수정이 일부 호스팅 환경에서 즉시 반영되지 않는
  사례가 확인되어 원인을 추가로 좁혔습니다. WP_Query에는 `pre_get_posts` 외에도
  `posts_pre_query`라는, 캐싱 플러그인들이 쿼리 결과를 통째로 캐시해뒀다가 돌려주는
  데 쓰는 지점이 있고 이 역시 필터 배제 옵션과 무관하게 항상 실행됩니다. 사이트맵
  콘텐츠 쿼리 실행 중에는 이 지점도 함께 무효화하도록 보강했습니다.
* (버그 수정) 이 플러그인은 예전부터 "글이 발행/수정/삭제될 때만 사이트맵 캐시를
  비운다"는 방식이었는데, 코드 자체가 바뀌는 업데이트 시점에는 캐시를 비우지
  않아 앞단의 서버/CDN 캐시에 저장된 "수정 전 버전이 만든" 사이트맵 사본이 계속
  나갈 수 있었습니다. 이제 플러그인 버전이 바뀔 때마다 사이트맵 관련 캐시를 자동으로
  함께 비워, 업데이트 직후 관리자가 별도로 캐시를 비우지 않아도 최신 사이트맵이
  바로 반영됩니다.

= 1.0.7 =
* (버그 수정) 글마다 언어가 태그되는 다국어 사이트에서, 번역 관련 플러그인이 워드프레스의
  pre_get_posts 훅으로 "글 목록을 조회하는 모든 쿼리"에 개입해 기본적으로 한 언어만
  보여주도록 걸러내고 있었고, 이 때문에 사이트맵이 번역된 글들을 통째로 누락시키고
  있었습니다(예: 발행글 1,067개 중 631개만 사이트맵에 반영되어 마지막 페이지가
  등록만 되고 내용이 비는 문제로 이어짐). 다른 플러그인의 코드는 건드리지 않고,
  이 플러그인이 사이트맵용 글 목록을 조회하는 그 순간에만 그런 개입을 일시적으로
  무효화한 뒤 즉시 원상복구하는 방식으로 수정했습니다 — 그 결과 번역된 글도 각각
  고유 URL로 사이트맵에 전부 포함됩니다.

= 1.0.4 =
* (버그 수정) 글이 500개를 넘어 사이트맵이 여러 페이지(sitemap-posts-2.xml, -3.xml…)로
  나뉘는 사이트에서, 대량 이관 등으로 워드프레스의 글 개수 캐시가 실제 값과 어긋나면
  마지막 사이트맵 페이지가 등록은 되었는데 안에 URL이 하나도 없는 상태로 나오는
  경우가 있었습니다. 페이지 수 계산을 캐시를 거치지 않는 실시간 값으로 통일해
  수정했습니다.
* (버그 수정) 새 사이트맵 페이지가 생겨도, 관리자가 "사이트 에러체크" 화면을 직접
  열기 전까지는 매일 자동 점검 대상 목록이 갱신되지 않아 새로 생긴 페이지가 자동
  점검·이메일 알림에서 빠지는 사각지대가 있었습니다. 이제 이 목록을 매시간 자동으로
  최신화합니다(무료 동기화이며, 하루 1회인 유료 점검 횟수 자체는 그대로입니다).
* (버그 수정) "사이트 에러체크" 화면에서, 아래 "사이트맵 지금 상태"(그 자리에서 실시간
  재확인, 무료)가 문제를 보여주는데도 위 배지/"마지막 점검" 요약은 지난밤 자동 점검
  시점 기준의 "정상"을 그대로 보여줘 서로 모순돼 보이는 문제를 수정했습니다. 이제
  실시간 확인에서 문제가 발견되면 지난 점검 결과와 무관하게 배지에도 즉시 반영됩니다.

= 1.0.3 =
* (심사 대응) "사이트 에러체크" 화면의 클라우드 통신 전, 명시적인 동의(옵트인) 절차를
  추가했습니다. 기본값은 꺼짐이며, 동의하기 전까지는 도메인/사이트맵 정보가 서버로
  전송되지 않습니다.
* (심사 대응) 남아있던 인라인 &lt;script&gt; 태그를 wp_enqueue_script()/wp_add_inline_script()
  방식으로 교체했습니다.
* (심사 대응) 구조화 데이터(JSON-LD) 출력 시 이스케이프 방식을 강화해, 글 제목 등에
  특수문자가 있어도 스크립트 태그가 조기 종료될 수 없도록 수정했습니다.
* (심사 대응) 다른 플러그인과 충돌 위험이 있던 내부 접두어(3자)를 더 긴 고유 접두어로
  전면 교체했습니다. 이미 저장돼 있던 설정값/점검 이력/링크 후보 로그는 업데이트 시
  자동으로 새 이름으로 옮겨져 그대로 유지됩니다.

= 1.0.2 =
* 결제 수단으로 Lemon Squeezy를 추가했습니다(패들과 병행 지원, 서버 설정에 따라 둘 중 하나만
  활성화됩니다). 결제창 진입 방식만 바뀌며, 크레딧/요금제 화면과 사용 방법은 동일합니다.
* 테마에 기본 적용되는 네이버 애널리틱스, 구글 태그 매니저(GTM), 구글 애널리틱스(GA4)
  추적 코드를 최신 값으로 교체했습니다.

= 1.0.1 =
* 클라우드 서버 주소 표기를 soho-api.luxdigest.com으로 갱신했습니다(기능 변경 없음).

= 1.0.0 =
* 최초 릴리스. 구조화 데이터 자동 출력, 실시간 체크리스트, 발행글 일괄 점검 대시보드,
  일반/뉴스 XML 사이트맵, 노인덱스 관리(날짜·작성자·태그·검색·카테고리·커스텀
  택소노미별 색인 유지/전체 노인덱스/페이지네이션만 노인덱스 3택 1 지원), 301/302
  리다이렉트, 내부/외부링크 관리(수동/자동), 사이트 에러체크(검색엔진주요항목:
  robots.txt·사이트맵 매일 자동 점검 및 변경 감지), 감성형 포스팅 체크를 포함합니다.
