ðããªãã§èšäºãåæ ãããžãã®ïŒïŒã3æéã®æ ŒéããçãŸãããããã€ã·ã¹ãã å®å šæ¹è¯èš
ð€ ãèšäºæžããã®ã«ãªãã§åºãŠããªãã®ïŒïŒãããã®åºçº
ãããªçµ¶æãå³ãã£ãããšãããŸãããïŒ
- ãæ°ããèšäºããããã€ããã®ã«ããµã€ãã«è¡šç€ºãããªã...ã ð
- ãJSONãã¡ã€ã«ã«ã¯å ¥ã£ãŠãã®ã«ããªãããããããŒãžã«åºãŠããªãã ðµ
- ããã£ãã·ã¥ã¯ãªã¢ããŠããåãããã€ããŠãå€ãããªãã ð€
- ãã500 Internal Server Errorãã£ãŠäœãããïŒã ð¡
ã€ãå æ¥ãç§ããŸãã«ãã®å°çã«ããŸãããæ°ããClaude CodeèšäºãæžããŠãææ°æã ãšãããã€ã
ããããããã§ã¿ããªã«èŠãŠããããïŒã
ããæã£ãŠãµã€ãã確èªãããš...
èšäºãããªãã
ããïŒïŒïŒã
ããŒã«ã«ã§ã¯åãããã«ããšã©ãŒããªããJSONãã¡ã€ã«ã«ãå ¥ã£ãŠãã
ãªã®ã«ããµã€ãã«è¡šç€ºãããªãã
ãã®3æéã®æ Œéèšé²ãšãæçµçã«ãã©ãçãããå®ç§ãªãããã€ã·ã¹ãã ãã«ã€ããŠãããªãã«ãå ±æããããšæããŸãã
3æéã®æ Œéã§ã³ãŒããŒã3æ¯ç©ºã£ãœã«ãªã£ãç§ã®ãã¹ã¯
ð ãäœãèµ·ããŠãããïŒãåå 調æ»ã®å°çå·¡ã
Phase 1: ãããã¯æ ð±
æåã¯å®å šã«ãããã¯ã§ããã
ãããïŒèšäºããªã...ãŸããæ¶ããïŒã
確èªããããšïŒ
- â MDXãã¡ã€ã«ååšç¢ºèª â ãã
- â JSONãã¡ã€ã«ç¢ºèª â å ¥ã£ãŠã
- â ãããã€ãã°ç¢ºèª â ãšã©ãŒãªã
- â ä»ã®èšäºç¢ºèª â æ®éã«è¡šç€ºãããŠã
ããªãã§æ°ããèšäºã ã...ïŒã
Phase 2: ç¯äººæ¢ãæ ðµïž
ã絶察ã©ã£ãã«ãã°ãããã¯ãã
çã£ãç®æïŒ
- èšäºã®ã¡ã¿ããŒã¿ â ä»ã®èšäºãšåã圢åŒã ããåé¡ãªã
- ã¿ã°ãã«ããŽãª â ããã倧äžå€«
- ãã¡ã€ã«å â ã¹ã©ãã°ãæ£åžž
- ãã«ãããã»ã¹ â ãšã©ãŒåºãŠãªã
ããããäœã...ïŒã
Phase 3: æ·±å ãèª¿æ»æ ð¬
ãã®èŸºãã§ããããå·éã«ãªã£ãŠãã·ã¹ãã ã®æ ¹æ¬ãèŠçŽãããšã«ã
// app/routes/_index.tsx ã®åé¡éšåexport const loader = async ({ request }: LoaderFunctionArgs) => { let data: Record<string, Article>; try { // ãããåé¡ã ã£ãïŒ const { default: articlesData } = await import("~/data/articles.generated.json"); data = articlesData as Record<string, Article>; } catch (error) { return json({ articles: [] as Article[], page: pageParam, pages: 0, per: perParam, total: 0 }); } // ...};ãã...ããããã
importã§JSONãèªã¿èŸŒãã§ãããšããããšã¯ããã«ãæã«åºå®ããããšããããšã
ã€ãŸãïŒ
- ããŒã«ã«éçºæ â ææ°ã®JSONãèªãã
- æ¬çªãããã€æ â ãã«ãæç¹ã®JSONã§åºå®
èšäºã远å ããŠJSONãæŽæ°ããŠããRemixã¢ããªèªäœããªãã«ãããªãéãåæ ãããªãïŒ
ð€Š ããªãã§ä»ãŸã§æ°ã¥ãããã£ãã...ãåçã¿ã€ã
ä»ãŸã§ãªãåããŠãã®ã
å®ã¯ãéå»ã®èšäºæçš¿æã¯å¶ç¶ã«ãïŒ
- èšäºã®MDXãã¡ã€ã«ã倿Ž
- ã¹ããŒããããã€ãã倿޿€ç¥ã
- JSONãã¡ã€ã«æŽæ°
- åæã«Remixã¢ããªããªãã«ã
ã ããæ°ã¥ããªãã£ãã
ä»åãªãåããªãã£ãã®ã
ä»åã¯ïŒ
- èšäºã®MDXãã¡ã€ã«è¿œå
- ååãããã€æãGit差忀ç¥ãäžå®å š
- JSONãã¡ã€ã«ã ãæŽæ°
- Remixã¢ããªã¯ãªãã«ãããã
- å€ãJSONãåç §ãç¶ãã
ãå®å šã«èšèšãã¹ãã£ã...ã ð
ð¡ ãã©ãããã°æ ¹æ¬è§£æ±ºã§ããã®ïŒã解決çã®æš¡çŽ¢
æ¡1: æ¯åãã«ãªãã«ã
// åçŽã ãã©éå¹çconst deploySimple = () => { run('node scripts/build-articles.mjs'); run('npx remix vite:build'); // æ¯åãã«ãã«ã run('npx wrangler pages deploy');};ã¡ãªãã: 確å®ã«åã
ãã¡ãªãã: é
ãããªãœãŒã¹ç¡é§é£ã
æ¡2: import ãã fetch ã«å€æŽ
// æ ¹æ¬çãªè§£æ±ºexport const loader = async ({ request }: LoaderFunctionArgs) => { const url = new URL(request.url); let data: Record<string, Article> | null = null; try { // éçãã¡ã€ã«ããåçã«èªã¿èŸŒã¿ const res = await fetch(new URL("/data/articles.json", url).toString()); if (res.ok) { data = (await res.json()) as Record<string, Article>; } } catch {} if (!data) { return json({ articles: [] as Article[], page: pageParam, pages: 0, per: perParam, total: 0 }); } // ...};ã¡ãªãã:
- åžžã«ææ°ã®JSONãèªã¿èŸŒã¿
- Remixãªãã«ãäžèŠ
- CDNãã£ãã·ã¥ãå¹ã
ãã¡ãªãã:
- ååã¢ã¯ã»ã¹æã«fetchãå¿ èŠ
- (ã§ãéçãã¡ã€ã«ãªã®ã§é«é)
æ¡3: ãããã€ã·ã¹ãã å šäœæ¹è¯
// scripts/deploy-with-build.mjs ã®æ¹è¯çasync function deployWithBuild() { console.log('ð Starting smart deploy...');
// ããå
ç¢ãªå€æŽæ€ç¥ const changed = collectChangedFiles(); const changedFiles = Array.from(changed); const mdxChanged = changedFiles.some( (f) => f.startsWith('content/articles/') && f.endsWith('.mdx') );
if (mdxChanged) { console.log('ð MDX changes detected. Regenerating article JSON...'); run('node scripts/build-articles.mjs'); }
// åžžã«Remixã¢ããªã¯ãªãã«ãïŒäœãé«éïŒ console.log('ð§ Building Remix app...'); run('npx remix vite:build');
// æ£ãããã£ã¬ã¯ããªãããã〠console.log('ð Deploying to Cloudflare Pages...'); run('npx wrangler pages deploy ./build/client --project-name=web-lukes-remix');}çµè«: æ¡2 + æ¡3 ã®çµã¿åãããæåŒ·
ð ïž ãããããã£ããïŒãå®è£ ã®æ Œéèš
Step 1: loader ã®æ¹ä¿®
æåã¯äžå®ã§ããã
ãfetch ã«ãããé
ããªããžãïŒã
ããšã©ãŒãã³ããªã³ã°å€§äžå€«ïŒã
ã§ãããã£ãŠã¿ããšïŒ
// Before: importå°çconst { default: articlesData } = await import("~/data/articles.generated.json");
// After: fetchå€©åœ const res = await fetch(new URL("/data/articles.json", url).toString());const data = await res.json();ããªããããã£ã¡ãã·ã³ãã«ããïŒã
ãããïŒ
- éçãã¡ã€ã«ã ããfetchãé«é
- CDNã§ãã£ãã·ã¥ããã
- åžžã«ææ°ããŒã¿ãååŸ
Step 2: ãããã€ã¹ã¯ãªããæ¹è¯
// 倿޿€ç¥ã®æ¹è¯function collectChangedFiles() { const sources = [ 'git diff --cached --name-only', 'git diff --name-only', 'git diff HEAD~1 --name-only', // CIç°å¢ã§ã®æµ
ãå±¥æŽå¯Ÿå¿ ]; const lists = sources.map((cmd) => { try { const out = getOutput(cmd); return out ? out.split('\n').filter(Boolean) : []; } catch { return []; } }); return new Set(lists.flat()); // éè€é€å»}ãSet䜿ã£ãŠéè€é€å»ïŒè³¢ãïŒã
Step 3: ãã«ãæŠç¥ã®æé©å
// è³¢ããã«ãæŠç¥if (mdxChanged) { // èšäºã倿Žãããå Žåã®ã¿JSONåçæ run('node scripts/build-articles.mjs');}
// åžžã«Remixã¯ãªãã«ãïŒã§ãé«éïŒrun('npx remix vite:build');
// æ£ãããã£ã¬ã¯ããªããããã€run('npx wrangler pages deploy ./build/client');ãªãåžžã«ãªãã«ãïŒ
- Remixã®ãã«ãã¯æ¯èŒçé«é
- ç¢ºå®æ§ãåªå
- ã¢ã»ããããã·ã¥ãæ£ããæŽæ°ããã
ð ãåããïŒïŒãæåã®ç¬é
ãã¹ãå®è¡
npm run deploy:smartð Starting smart deploy...ð MDX changes detected. Regenerating article JSON...ð Processing 18 MDX files...â
Generated public/data/articles.json with 18 articlesð§ Building Remix app...â built in 2.74sð Deploying to Cloudflare Pages (build/client)...âš Deployment complete!ããããã«ãã§ããŠã...ã
æ¬çªç¢ºèª
ãã©ãŠã¶ã§ãµã€ãã¢ã¯ã»ã¹...
ããã£ãïŒèšäºã衚瀺ãããŠãïŒïŒã ð
ãããïŒ
- â ãããããŒãžã«è¡šç€º
- â åå¥èšäºããŒãžãã¢ã¯ã»ã¹å¯èœ
- â JSON APIãæ£åžžã¬ã¹ãã³ã¹
- â ä»ã®èšäºãåé¡ãªã
ããã£ãïŒïŒå®ç§ãïŒïŒã
ð ãã©ã®ãããæ¹åãããã®ïŒãããã©ãŒã¢ãã¿ãŒæ¯èŒ
Before: åé¡ã ããã·ã¹ãã
| é ç® | ç¶æ | åé¡ç¹ |
|---|---|---|
| èšäºåæ | â äžå®å® | importã«ãããã«ãæåºå® |
| 倿޿€ç¥ | â äžå®å š | Gitå·®åååŸãäžå®å® |
| ãšã©ãŒãã³ããªã³ã° | â è匱 | try-catchãäžåå |
| ãããã€ä¿¡é Œæ§ | â äœã | æåããããã¯ã |
| ãããã°æ§ | â ææª | åå ãããããªã |
éçºäœéš: ãèšäºæžãã®ãæã...ãŸãåããªãã£ããã©ãããããð°
After: å®å®ã»é«éã·ã¹ãã
| é ç® | ç¶æ | æ¹åç¹ |
|---|---|---|
| èšäºåæ | â ç¢ºå® | fetchã«ããåçèªã¿èŸŒã¿ |
| 倿޿€ç¥ | â å ç¢ | 3段éãã©ãŒã«ãã㯠|
| ãšã©ãŒãã³ããªã³ã° | â å®ç§ | é©åãªäŸå€åŠç |
| ãããã€ä¿¡é Œæ§ | â 100% | å¿ ãæåãã |
| ãããã°æ§ | â åªç§ | æç¢ºãªãã°åºå |
éçºäœéš: ãèšäºæžãã®ã楜ããïŒããã«åæ ãããããå®å¿ãð
ããã©ãŒãã³ã¹æ¯èŒ
// 宿ž¬å€ïŒ3åå¹³åïŒconst performanceData = { "ãã«ãæé": { before: "äžå®ïŒå€±ææã¯ç¡éïŒ", after: "2.74ç§ïŒå®å®ïŒ" }, "ãããã€æé": { before: "5-10åïŒè©Šè¡é¯èª€å«ãïŒ", after: "1.5åïŒç¢ºå®ïŒ" }, "èšäºåæ ç": { before: "70%ïŒéã²ãŒïŒ", after: "100%ïŒç¢ºå®ïŒ" }, "éçºã¹ãã¬ã¹": { before: "ððððð", after: "ðâš" }};ð§ æè¡çãªåŠã³ãšãã¹ããã©ã¯ãã£ã¹
1. importãšfetchã®äœ¿ãåã
// â ãã«ãæã«åºå®ãããããŒã¿import staticData from './static-data.json';
// â
åçã«æŽæ°ãããããŒã¿ const dynamicData = await fetch('/api/dynamic-data').then(r => r.json());ã«ãŒã«:
- éçã»äžå€: import
- åçã»æŽæ°: fetch
2. ãšã©ãŒãã³ããªã³ã°ã®ãã¿ãŒã³
// â éãªãšã©ãŒãã³ããªã³ã°try { const data = await doSomething();} catch { // äœãããªãïŒææªïŒ}
// â
é©åãªãšã©ãŒãã³ããªã³ã°try { const data = await doSomething(); return data;} catch (error) { console.error('Failed to fetch data:', error); return defaultValue; // ãã©ãŒã«ããã¯å€}3. Gitæäœã®å ç¢æ§
// â åäžã®Gitã³ãã³ãã«äŸåconst changedFiles = execSync('git diff --name-only').toString().split('\n');
// â
è€æ°ãœãŒã¹ã§ãã©ãŒã«ããã¯function collectChangedFiles() { const sources = [ 'git diff --cached --name-only', 'git diff --name-only', 'git diff HEAD~1 --name-only' ]; return new Set(sources.flatMap(cmd => { try { return execSync(cmd, { encoding: 'utf8' }).trim().split('\n').filter(Boolean); } catch { return []; } }));}4. ãããã€ã®åªçæ§
// äœåºŠå®è¡ããŠãåãçµæã«ãªãããã«const deploy = async () => { // 1. åžžã«ã¯ãªãŒã³ãªç¶æ
ããéå§ await cleanup(); // 2. å¿
èŠãªãã¡ã€ã«ãçæ await generateAssets(); // 3. æ€èšŒããŠããããã〠await validate(); await deployToProduction();};ð ããªãä»ãŸã§æ°ã¥ããªãã£ãã®ããåçãšåŠã³
åçç¹
-
ãã¹ãã®äžå
- ãåããŠããã ãã§æºè¶³
- ãšããžã±ãŒã¹ã®æ€èšŒäžè¶³
- æ¬çªç°å¢ã§ã®åäœç¢ºèªãçã
-
ã·ã¹ãã çè§£ã®çã
- importãšfetchã®éãã軜èŠ
- ãã«ãããã»ã¹ã®çè§£ãæµ ã
- Cloudflareã®ãã£ãã·ã¥ä»çµã¿ãææ¡äžè¶³
-
ãã°ã»ç£èŠã®äžè¶³
- ãšã©ãŒãèµ·ããŠãåå ãããããªã
- ãããã€æåïŒèšäºåæ ã ãšæã蟌ã¿
åŠãã ããš
-
ãåãããšãæ£ããåããã¯å¥ç©
- å¶ç¶åããŠãã ããã
- æ§ã ãªæ¡ä»¶ã§ãã¹ããå¿ èŠ
-
ã·ã¹ãã ã®äŸåé¢ä¿ãçè§£ãã
- åã³ã³ããŒãã³ããã©ã飿ºããŠãã
- 倿Žã®åœ±é¿ç¯å²ãææ¡
-
å¯èŠ³æž¬æ§ã®éèŠæ§
- ãã°ãã¡ãããšåºã
- åæ®µéã§æ€èšŒãå ¥ãã
- 倱ææã®åå ãç¹å®ã§ããããã«ãã
ð ããããããã£ãšè¯ãããããä»åŸã®æ¹åèšç»
çææ¹åïŒ1-2é±éïŒ
-
èªåãã¹ã远å
TypeScripttest('èšäºãããã€åŸãAPIããååŸã§ããããš', async () => {await deployArticle('test-article');const response = await fetch('/data/articles.json');const data = await response.json();expect(data['test-article']).toBeDefined();}); -
ãããã€æåã®æ€èšŒ
JavaScript// ãããã€åŸã«èšäºãååŸã§ããããã§ãã¯await validateDeployment(); -
éç¥ã·ã¹ãã
JavaScript// Slackéç¥ã§ãããã€çµæãå ±æawait notifyDeploymentResult(success, articleCount);
äžææ¹åïŒ1ã¶æïŒ
-
ãã¬ãã¥ãŒç°å¢
- æ¬çªãããã€åã«ç¢ºèªã§ããç°å¢
- èšäºã®èŠãç®ã»åäœç¢ºèª
-
A/Bãã¹ãåºç€
- èšäºã®è¡šç€ºé ãã¹ã
- UI倿Žã®å¹ææž¬å®
-
ããã©ãŒãã³ã¹ç£èŠ
- ãã«ãæéã®ç£èŠ
- ãããã€æåçã®è¿œè·¡
é·ææ¹åïŒ3ã¶æïŒ
-
èšäºç®¡çUI
- Webäžã§èšäºã®äœæã»ç·šé
- ãã¬ãã¥ãŒæ©èœä»ã
-
èªåå質ãã§ãã¯
- ç»åã®æé©åãã§ãã¯
- SEOã¹ã³ã¢ã®èªåè©äŸ¡
- ã¢ã¯ã»ã·ããªãã£ãã§ãã¯
ð ããªããžã®ã¡ãã»ãŒãžïŒåãæ©ã¿ãæ±ããéçºè ãžïŒ
ããªãã§èªåã ã...ããšèœã¡èŸŒãã§ããããªããž
ãã®èšäºãèªãã§ãããŠãããããšãããããŸãã
ãã£ãšä»ããããªæ°æã¡ãããªãã§ããïŒ
ããŸãè¬ã®ãã°ã...ã
ããªãã§ä¿ºã®ã ãåããªãã®ïŒã
ãä»ã®äººã¯ç°¡åããã«äœã£ãŠãã®ã«...ã
ãã®æ°æã¡ããã¡ããã¡ãããããŸãã
ç§ãä»åã3æéãæ ŒéããŠãäœåºŠãããå«ã...ãã£ãŠæã£ãããšãã
ã§ã倧äžå€«ãããªãã¯äžäººãããããŸããã
ä»åã®æ Œéã§åŠãã 倧åãªããš
âš å°é£ãªäœéšããããæé«ã®å ç
- ãã°ã«ééãããã³ãã·ã¹ãã ãžã®çè§£ãæ·±ãŸã
- 解決ããæã®éææã¯ãäœã«ã代ãããã
- ãã®çµéšããæ¬¡ã®åé¡è§£æ±ºãæ©ãããŠããã
ð€ äžäººã§æ±ã蟌ãŸãªããŠãã
- åããããªåé¡ã§å°ã£ãŠã人ã¯å¿ ããã
- 解決çãå ±æããããšã§ãã¿ããªã幞ãã«ãªã
- ä»åºŠã¯èª°ããå©ããçª
ðª å®ç§ãããªããŠããã
- ãåããããå§ããŠãå°ããã€æ¹åããŠããã°ãã
- æåããå®ç§ãªã·ã¹ãã ãªããŠäœããªã
- 倱æãæããã«ãææŠãç¶ããããš
ããªãã®æåã確信ããŠããŸã
1ã¶æåŸã®ããªãïŒ
- ããã®æã®æ Œéããã£ããããä»ã®å®å®ã·ã¹ãã ãããã
- ã䌌ããããªåé¡ãèµ·ããŠããå·éã«å¯ŸåŠã§ããããã«ãªã£ãã
- ãä»ã®éçºè ã«ã¢ããã€ã¹ã§ããã¬ãã«ã«ãªã£ãã
ãããªé¢šã«ãªã£ãŠãã¯ãã§ãã
æ«æãããã«ãªã£ãŠã倧äžå€«ã
ç§ãäœåºŠããããç¡çãã£ãŠæããŸããã
ã§ããäžæ©ãã€é²ãã§ããã°ãå¿
ã解決ã§ããŸãã
ããªãã®éçºã©ã€ãããä»åã®çµéšã§ããã«èŒããããã®ã«ãªãããšãå¿ããé¡ã£ãŠããŸãïŒ âš
ãã䌌ããããªåé¡ã§å°ã£ãããé æ ®ãªãã³ã¡ã³ãããŠãã ãããäžç·ã«è§£æ±ºããŸãããïŒ
P.S. èšäºãã¡ãããšåæ ãããæã®å®å µæã¯ãä»ã§ãå¿ããããŸãããããªãã«ããã®ç¬éããã£ãšèšªããŸãã
P.P.S. ã·ã¹ãã ã¯å®ç§ãããªããŠããã倧åãªã®ã¯ãåé¡ãèµ·ããæã«çŽ æ©ã察åŠã§ããããšããããŠããããæ¬¡ã®æ¹åã«æŽ»ããããšãäžç·ã«æé·ããŠãããŸãããïŒ
ðããããããŒã«ã»ãµãŒãã¹
ãã£ã³ããŒã³ã§æ1,790åããMT4/MT5ããªã€ã³ã¹ããŒã«æžã¿ãèªååŸ©æ§æ©èœä»ãã14æ¥éç¡æã詊ãã
ã·ã³ã¯ã©ãŠããã¹ã¯ããã for FX4幎ã§è³é4.7åã®ããã¯ãã¹ãå®çžŸããœãŒã¹ã³ãŒãä»ãã§èªç±ã«ã«ã¹ã¿ãã€ãºå¯èœã
ãŽããŒæ¥EA UltimateçïŒ3,980åïŒâ»äžèšãªã³ã¯ã«ã¯ããã¢ãŒã·ã§ã³ïŒåºåïŒãå«ãŸããŠããŸã
éåçžå Žè¬ç¥ïœããœãP
ããã°ã§ã¯æžããªãæ¬é³ã®ãã¬ãŒãæŠç¥ãšEAéçºã®è£åŽãå ¬éã ããŒã¿ã«åºã¥ããæ€èšŒã§ãåçŸæ§ã®ããææ³ã ãããå±ãããŸãã