DEV Community

럭키리치
럭키리치

Posted on

How I ship a production website in 7 days with one HTML file and a Python SEO generator

I run a one-person web agency called the vibe company that ships real client sites in 7 days. Live: zamhwa-studio.netlify.app. This post is the stack and the actual code that makes the 7-day promise not a lie.

TL;DR stack

  • 1 HTML file per client (yes, still in 2026)
  • Netlify for hosting, HTTPS, forms, analytics
  • Python 3 script that generates topical SEO cluster pages
  • Playwright for cross-viewport QA screenshots
  • Claude for first drafts (I rewrite ~60% of the output)

Zero frameworks. Zero build step beyond netlify deploy. Zero CMS.

Why no framework

I'm not anti-framework. I'm anti-framework-for-brochure-sites. Ninety percent of small business sites are:

  1. Hero
  2. Services
  3. Portfolio
  4. Pricing
  5. Contact form
  6. Footer

A single HTML file with Tailwind utility classes compiled in via CDN ships in 3 days. The same site in Next.js ships in 6 and costs me Vercel time I can't bill for.

The rule I use: if the client updates content less than once a month, they don't need a CMS.

The Python SEO generator (the actual code)

This is the piece that took me from "pretty site" to "site that Google can place in context." The idea: generate a small cluster of topically related static pages so each page's internal links reinforce the main site's authority.

# seo_gen.py — minimal version, ~80 lines
from pathlib import Path
import json, html

TEMPLATE = """<!doctype html>
<html lang="{lang}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>{title}</title>
  <meta name="description" content="{description}">
  <link rel="canonical" href="{canonical}">
  <script type="application/ld+json">{schema}</script>
  <link rel="stylesheet" href="/style.css">
</head>
<body>
  {header}
  <main>
    <h1>{h1}</h1>
    {body}
    <aside class="related">
      <h2>Related</h2>
      <ul>{related_links}</ul>
    </aside>
  </main>
  {footer}
</body>
</html>
"""

def render_page(page, all_pages, header, footer):
    related = [p for p in all_pages if p["slug"] in page["internal_links"]]
    related_links = "".join(
        f'<li><a href="/{p["slug"]}">{html.escape(p["title"])}</a></li>'
        for p in related
    )
    schema = json.dumps({
        "@context": "https://schema.org",
        "@type": "Article",
        "headline": page["h1"],
        "description": page["description"],
    })
    return TEMPLATE.format(
        lang=page.get("lang", "ko"),
        title=page["title"],
        description=page["description"],
        canonical=f'https://zamhwa-studio.netlify.app/{page["slug"]}',
        schema=schema,
        header=header,
        h1=page["h1"],
        body=page["body_html"],
        related_links=related_links,
        footer=footer,
    )

def build(pages_json="pages.json", out_dir="dist"):
    pages = json.loads(Path(pages_json).read_text(encoding="utf-8"))
    header = Path("partials/header.html").read_text(encoding="utf-8")
    footer = Path("partials/footer.html").read_text(encoding="utf-8")
    out = Path(out_dir)
    out.mkdir(exist_ok=True)
    sitemap = ['<?xml version="1.0" encoding="UTF-8"?>',
               '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
    for page in pages:
        (out / f'{page["slug"]}.html').write_text(
            render_page(page, pages, header, footer), encoding="utf-8"
        )
        sitemap.append(
            f'<url><loc>https://zamhwa-studio.netlify.app/{page["slug"]}</loc></url>'
        )
    sitemap.append("</urlset>")
    (out / "sitemap.xml").write_text("\n".join(sitemap), encoding="utf-8")

if __name__ == "__main__":
    build()
Enter fullscreen mode Exit fullscreen mode

pages.json looks like this:

[
  {
    "slug": "guide/cost-2026",
    "title": "홈페이지 제작 비용 2026 | the vibe company",
    "h1": "2026 홈페이지 제작 비용 완전 가이드",
    "description": "실제 견적서와 패키지별 단가.",
    "body_html": "<p>...</p>",
    "internal_links": ["guide/domain", "guide/seo-checklist"]
  }
]
Enter fullscreen mode Exit fullscreen mode

Run python seo_gen.pydist/ has 8 HTML files plus sitemap.xmlnetlify deploy --prod --dir=dist. Done.

The live cluster is here if you want to see the output:

The Playwright QA runner

I never deploy without running this:

// qa.js
const { chromium } = require('playwright');

const viewports = [
  { name: 'mobile', width: 375, height: 812 },
  { name: 'tablet', width: 768, height: 1024 },
  { name: 'desktop', width: 1440, height: 900 },
];

const urls = [
  'https://zamhwa-studio.netlify.app/',
  'https://zamhwa-studio.netlify.app/guide/cost-2026',
  'https://zamhwa-studio.netlify.app/industries/cafe',
];

(async () => {
  const browser = await chromium.launch();
  for (const vp of viewports) {
    const ctx = await browser.newContext({ viewport: vp });
    for (const url of urls) {
      const page = await ctx.newPage();
      await page.goto(url, { waitUntil: 'networkidle' });
      const slug = url.replace(/[^a-z0-9]/gi, '_');
      await page.screenshot({ path: `shots/${vp.name}_${slug}.png`, fullPage: true });
      await page.close();
    }
    await ctx.close();
  }
  await browser.close();
})();
Enter fullscreen mode Exit fullscreen mode

Before every client call I flip through the shots. In the last month this has caught iOS Safari button cutoff, a mobile-only stacked nav regression, and one CDN image cache that was serving a blurry variant.

Where Claude actually helps (and doesn't)

Helps a lot:

  • First draft of copy in both EN and KR
  • Writing Python glue scripts like the one above
  • Debugging CSS weirdness at 2am
  • Explaining error messages from Netlify build logs

Does not help:

  • Deciding the pricing tiers
  • Choosing what goes above the fold for a specific client
  • Anything involving the client's actual business

The honest version: it's a human-run shop where the human got faster. That's enough.

What this site ranks for (after 4 weeks)

  • "홈페이지 제작 비용" (home-page production cost, Korean) — page 2 on Naver
  • "카페 홈페이지 제작" — page 1 on Naver, position 6
  • "netlify korean agency" — position 2 on Google

Not massive, but the trajectory is up, and every cluster page adds link equity to the root.

If you want to poke at the live site

Main: zamhwa-studio.netlify.app
Pricing guide with real numbers: cost-2026

Happy to answer stack questions in the comments. Especially interested in any Naver SEO specifics that aren't obvious from the Google side — that's still the part I'm learning in public.

Top comments (0)