Skip to content

ksh3kim/GDSS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

82 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๊ฑดํ”„๋ผ ๊ฐ€์ด๋“œ (Gunpla Guide) ๐Ÿ”

๊ฑดํ”„๋ผ ์ „ ์ œํ’ˆ์„ ์ •๋ฐ€ ๋ถ„๋ฅ˜ํ•˜์—ฌ ๋‹น์‹ ์—๊ฒŒ ๋งž๋Š” ์ œํ’ˆ์„ ์ถ”์ฒœํ•˜๋Š” ์›น์‚ฌ์ดํŠธ์ž…๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ ์—†์ด ๋™์ž‘ํ•˜๋Š” ์ •์ (Static) ์›น์•ฑ์œผ๋กœ, ๋ฐ์ดํ„ฐ๋Š” JSON์œผ๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ๋Š” ์‹ค์ œ ์ฝ”๋“œ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๊ตฌํ˜„๋œ ๊ฒƒ, ๋ถ€๋ถ„ ๊ตฌํ˜„/๋ฏธ๊ตฌํ˜„์ธ ๊ฒƒ, ์ •์  ์•ฑ์˜ ํ•œ๊ณ„๋ฅผ ๊ตฌ๋ถ„ํ•ด ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค.

โœ… ๊ตฌํ˜„๋œ ๊ธฐ๋Šฅ

๐Ÿ”Ž ํƒ์ƒ‰ & ์ถ”์ฒœ

  • 25๊ฐœ ๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ(data/taxonomy.json): ๋“ฑ๊ธ‰, ์‹œ๋ฆฌ์ฆˆ, ๋‚œ์ด๋„, ๊ฐ€๋™์„ฑ, ์ƒ‰๋ถ„ํ• , ํ”„๋ ˆ์ž„, ์”ฐ ์˜์กด๋„ ๋“ฑ
  • ์ถ”์ฒœ ์ ์ˆ˜ ์ •๋ ฌ: ํ•„ํ„ฐ ํ™œ์„ฑ ์‹œ ๊ฐ€์ค‘์น˜ ๊ธฐ๋ฐ˜ ์ ํ•ฉ๋„ ์ ์ˆ˜์ˆœ ์ •๋ ฌ(js/recommendation.js)
  • ๊ฒ€์ƒ‰ & ์ž๋™์™„์„ฑ(๋ฐ์Šคํฌํ†ฑ): ์ œํ’ˆ๋ช…ยท๊ธฐ์ฒด๋ช…ยทํ˜•์‹๋ฒˆํ˜ธ, ํ•œ๊ธ€ ์ดˆ์„ฑ ๊ฒ€์ƒ‰, ๊ฒ€์ƒ‰ ๊ธฐ๋ก ์ €์žฅ
  • ์ •๋ ฌ: ๋ฐœ๋งค์ผยท์ด๋ฆ„ยท๊ฐ€๊ฒฉยท๋‚œ์ด๋„ยทํŒŒ์ธ ์ˆ˜ (โš ๏ธ ํ•„ํ„ฐ ํ™œ์„ฑ ์‹œ์—๋Š” ์ถ”์ฒœ ์ ์ˆ˜ ์ •๋ ฌ์ด ์šฐ์„  โ€” ์•„๋ž˜ ํ•œ๊ณ„ ์ฐธ๊ณ )
  • ๊ทธ๋ฆฌ๋“œ/๋ฆฌ์ŠคํŠธ ๋ทฐ ์ „ํ™˜, 24๊ฐœ ๋‹จ์œ„ "๋” ๋ณด๊ธฐ" ํŽ˜์ด์ง€๋„ค์ด์…˜
  • URL ๋”ฅ๋งํฌ: ํ•„ํ„ฐยท๊ฒ€์ƒ‰ ์ƒํƒœ๊ฐ€ URL ์ฟผ๋ฆฌ๋กœ ๊ธฐ๋ก๋˜์–ด, ๋งํฌ ๊ณต์œ /์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์ตœ์ดˆ ๋กœ๋“œ์—์„œ ๋ณต์›

๐ŸŽจ ํ…Œ๋งˆ ์‹œ์Šคํ…œ (์ „ ํŽ˜์ด์ง€ ์ ์šฉ)

  • ๊ธฐ๋ณธ 3์ข…: ๋ผ์ดํŠธ / ๋‹คํฌ / ํŠธ๋ฃจ ๋ธ”๋ž™(OLED)
  • ๊ฑด๋‹ด ์—๋””์…˜ 5์ข…: RX-78 ยท ์ƒค์•„ ์ „์šฉ ยท ์ง€์˜จ ยท ์œ ๋‹ˆ์ฝ˜ ยท ์—๋ฐ˜๊ฒŒ๋ฆฌ์˜จ โ€” ๊ทธ๋ผ๋ฐ์ด์…˜ยท์‹ฌ๋ณผยทํŒจํ„ด ํฌํ•จ ๋น„์ฃผ์–ผ ์•„์ด๋ดํ‹ฐํ‹ฐ
  • ์ปค์Šคํ…€ ํ…Œ๋งˆ: ํฌ์ธํŠธยท๊ฐ•์กฐยท๋ฐฐ๊ฒฝยท์นด๋“œยทํ…์ŠคํŠธ ์ƒ‰์ƒ ์ง์ ‘ ์ง€์ •(์Œ์˜ ์ž๋™ ํŒŒ์ƒ, ๋ณ€๊ฒฝ ์ฆ‰์‹œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ)
  • FOUC ๋ฐฉ์ง€: ํŽ˜์ธํŠธ ์ด์ „์— ์ €์žฅ๋œ ํ…Œ๋งˆ ์ ์šฉ(์–‘ ํŽ˜์ด์ง€ <head> ์ธ๋ผ์ธ ์Šคํฌ๋ฆฝํŠธ)

โญ ์ฆ๊ฒจ์ฐพ๊ธฐ & ๋น„๊ตํ•จ

  • ์นด๋“œยท์ƒ์„ธํŽ˜์ด์ง€ ํ† ๊ธ€, ๋ฐฐ์ง€ ์นด์šดํŠธ, localStorage ์˜์†ํ™”
  • ์ƒ์„ธ ์ŠคํŽ™ ๋น„๊ต ํ…Œ์ด๋ธ”: 3๊ฐœ ์„น์…˜(๊ธฐ๋ณธ/์กฐ๋ฆฝ/ํŠน์„ฑ) 19๊ฐœ ํ•ญ๋ชฉ, sticky ํ—ค๋”/์ฒซ ์—ด, ์ตœ๊ณ ยท์ตœ์ € ํ•˜์ด๋ผ์ดํŠธ, ์—ด๋ณ„ ์ œ๊ฑฐ ๋ฒ„ํŠผ (๋น„๊ตํ•จ์€ ์นด๋“œ ๋‚˜์—ด์ด ์•„๋‹Œ ํ…Œ์ด๋ธ” ์ „์šฉ ๋ทฐ)
  • ์ „์ฒด ์ดˆ๊ธฐํ™” ๋ฒ„ํŠผ: ํ‘ธํ„ฐ ํ•˜๋‹จ ์ €์‹œ์ธ์„ฑ ๋ฐฐ์น˜, ํ™•์ธ ๋Œ€ํ™”์ƒ์ž ํ›„ ์‹คํ–‰
  • ์„ ํƒ ํ•ด์ œ ์ฆ‰๊ฐ ๋ฐ˜์˜: ์ฆ๊ฒจ์ฐพ๊ธฐ/๋น„๊ตํ•จ ํƒญ์—์„œ ํ•ด์ œ ์‹œ ๋ชฉ๋กยทํ…Œ์ด๋ธ” ์ฆ‰์‹œ ๊ฐฑ์‹ 
  • ํฌ๋กœ์Šคํƒญ ๋™๊ธฐํ™”: storage ์ด๋ฒคํŠธ๋กœ ๋‹ค๋ฅธ ํƒญ์˜ ๋ณ€๊ฒฝ ์ฆ‰์‹œ ๋ฐ˜์˜

๐Ÿ•’ ์ตœ๊ทผ ์กฐํšŒ ์ œํ’ˆ

  • ์ตœ๋Œ€ 10๊ฐœ, ์ค‘๋ณต ์ œ๊ฑฐยท์ตœ์‹ ์ˆœ, ํ…์ŠคํŠธ ์—†๋Š” ์ธ๋„ค์ผ ์ŠคํŠธ๋ฆฝ(๋ฉ”์ธยท์ƒ์„ธ ์ƒ๋‹จ)

๐Ÿ“ฐ ์‹ ์ œํ’ˆ ยท ์†Œ์‹ (์•ฑ ๋‚ด ์•Œ๋ฆผ ํŒจ๋„)

  • ํ—ค๋” ์•Œ๋ฆผ ๋ฒจ + ์ฝ์ง€ ์•Š์Œ ๋ฐฐ์ง€, ํŒจ๋„ ์—ด๋žŒ ์‹œ ์ฝ์Œ ์ฒ˜๋ฆฌ
  • RSS ์กฐํšŒ: ํŽ˜์ด์ง€ ์ ‘์† ์‹œ(์บ์‹œ 30๋ถ„ ๊ฒฝ๊ณผ ํ›„)์™€ ์ˆ˜๋™ ์ƒˆ๋กœ๊ณ ์นจ(โ†ป) ์‹œ Bandai Hobby / GUNDAM.INFO ํ”ผ๋“œ ํ™•์ธ
    • โš ๏ธ ์ƒ์‹œ ๊ฐ์‹œยท๋ฐฑ๊ทธ๋ผ์šด๋“œ ํด๋งยทํ‘ธ์‹œ ์•Œ๋ฆผ์ด ์•„๋‹˜ โ€” ์—ด๋ ค ์žˆ๋Š” ํŽ˜์ด์ง€์—์„œ ์กฐํšŒํ•œ ์‹œ์ ์˜ ์†Œ์‹๋งŒ ํ‘œ์‹œ
  • 2๋‹จ ์ˆ˜์ง‘ ๊ตฌ์กฐ: ์ž์ฒด ์„œ๋ฒ„๋ฆฌ์Šค API(serverless/) ์šฐ์„  โ†’ ๋ฏธ๋ฐฐํฌ ์‹œ ๊ณต๊ฐœ CORS ํ”„๋ก์‹œ ํด๋ฐฑ โ†’ ์ „์ฒด ์‹คํŒจ ์‹œ ์บ์‹œ ํ‘œ์‹œ

๐ŸŒ ๋‹ค๊ตญ์–ด & ๊ธฐํƒ€

  • ํ•œ๊ตญ์–ด/์˜์–ด ํด๋ฆญ ์ฆ‰์‹œ ์ „ํ™˜(data/i18n.json) โ€” ์นด๋“œยทํ•„ํ„ฐยท๋น„๊ต ํ…Œ์ด๋ธ”ยท์•Œ๋ฆผ ๋“ฑ ์žฌ๋ Œ๋”
  • ์ƒ์„ธํŽ˜์ด์ง€ โ†’ ๋ฐ˜๋‹ค์ด ๊ณต์‹ ์„ค๋ช…์„œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋งํฌ(๋“ฑ๊ธ‰+ํ˜•์‹๋ฒˆํ˜ธ ํ‚ค์›Œ๋“œ)
  • shadcn/ui ์ฐธ๊ณ  ์ •์ œ: :focus-visible ๋ง, press ํ”ผ๋“œ๋ฐฑ, disabled/selection ์ผ๊ด€ํ™”
  • ๋ฐ˜์‘ํ˜•(๋ชจ๋ฐ”์ผ/ํƒœ๋ธ”๋ฆฟ/๋ฐ์Šคํฌํ†ฑ), ์ด๋ฏธ์ง€ loading="lazy", ์นดํ…Œ๊ณ ๋ฆฌ ์กฐํšŒ O(1) Map ์บ์‹œ

๐Ÿšง ๋ถ€๋ถ„ ๊ตฌํ˜„ / ๋ฏธ๊ตฌํ˜„

ํ•ญ๋ชฉ ์ƒํƒœ
๋ฒ”์œ„ ํ•„ํ„ฐ (์ถœ์‹œ์—ฐ๋„ยท๊ฐ€๋™์„ฑยท๋ถ€ํ’ˆ์ˆ˜ยท๋Ÿฌ๋„ˆ์ˆ˜) UI(์ž…๋ ฅ์ฐฝ+์Šฌ๋ผ์ด๋”)๋Š” ๋ Œ๋”๋˜์ง€๋งŒ ์ด๋ฒคํŠธ๊ฐ€ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•„ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ (setRangeFilter๋Š” ์กด์žฌํ•˜๋‚˜ ํ˜ธ์ถœ๋ถ€ ์—†์Œ)
์ถ”์ฒœ ์„ค๋ช… ํŒจ๋„ JS ํ•จ์ˆ˜๋งŒ ์กด์žฌ, HTML ์ปจํ…Œ์ด๋„ˆ(recommendationPanel) ์—†์Œ โ†’ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์ง€ ์•Š์Œ (์ ์ˆ˜ ์ •๋ ฌ์€ ๋™์ž‘)
๋น ๋ฅธ ๋ณด๊ธฐ(Quick View) ๋ชจ๋‹ฌ openQuickView ํ•จ์ˆ˜๋งŒ ์กด์žฌ, ์ง„์ž… ๋ฒ„ํŠผยท๋ชจ๋‹ฌ ๋งˆํฌ์—… ์—†์Œ โ†’ ๋ฏธ๋™์ž‘
๋ธŒ๋ผ์šฐ์ € ๋’ค๋กœ๊ฐ€๊ธฐ ์ƒํƒœ ๋ณต์› URL์€ replaceState๋กœ ๊ฐฑ์‹ ๋˜์ง€๋งŒ popstate ๋ฏธ์ฒ˜๋ฆฌ โ†’ ๋’ค๋กœ๊ฐ€๊ธฐ๋กœ ํ•„ํ„ฐ/ํƒญ ์ƒํƒœ๊ฐ€ ๋ณต์›๋˜์ง€ ์•Š์Œ
๋ชจ๋ฐ”์ผ ๊ฒ€์ƒ‰ ๊ฒ€์ƒ‰์–ด ์ ์šฉ๋งŒ ์ง€์› โ€” ์ž๋™์™„์„ฑยท๊ฒ€์ƒ‰ ๊ธฐ๋ก UI ๋ฏธ์ง€์›(๋ฐ์Šคํฌํ†ฑ ์ „์šฉ)
ํ•„ํ„ฐ ์ค‘ ์ผ๋ฐ˜ ์ •๋ ฌ ํ•„ํ„ฐ ํ™œ์„ฑ ์‹œ ์ถ”์ฒœ ์ ์ˆ˜ ์ •๋ ฌ์ด ๊ฐ•์ œ๋˜์–ด ์ •๋ ฌ ์…€๋ ‰ํŠธ๊ฐ€ ๋ฌด์‹œ๋จ
์ž๋™ํ™” ํ…Œ์ŠคํŠธ ์—†์Œ (์•„๋ž˜ "ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•"์˜ ์ˆ˜๋™ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋กœ ๊ฒ€์ฆ)
์ ‘๊ทผ์„ฑ focus ringยทaria-label ์ผ๋ถ€ ์ ์šฉ. ํƒญ ARIA(role/aria-selected), ๋ชจ๋‹ฌ ํฌ์ปค์Šค ํŠธ๋žฉ, ๋ฐฉํ–ฅํ‚ค ํƒ์ƒ‰ ๋ฏธ์ ์šฉ
๋„ค์ดํ‹ฐ๋ธŒ alert/confirm ๋น„๊ตํ•จ ์ •์› ์ดˆ๊ณผยท์ดˆ๊ธฐํ™” ํ™•์ธ์— ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋Œ€ํ™”์ƒ์ž ์‚ฌ์šฉ(์ปค์Šคํ…€ ํ† ์ŠคํŠธ ๋ฏธ์ ์šฉ)

โš ๏ธ ์ •์  ์›น์•ฑ์˜ ํ•œ๊ณ„

  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™์ž‘ ๋ถˆ๊ฐ€: ํŽ˜์ด์ง€๊ฐ€ ์—ด๋ ค ์žˆ์„ ๋•Œ๋งŒ ์ฝ”๋“œ ์‹คํ–‰ โ€” ์ฃผ๊ธฐ์  ์ˆ˜์ง‘ยท์„œ๋ฒ„ ํ‘ธ์‹œ ๋ถˆ๊ฐ€
  • CORS: ๋ธŒ๋ผ์šฐ์ €์—์„œ ์™ธ๋ถ€ RSS๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Œ โ†’ ์„œ๋ฒ„๋ฆฌ์Šค API ๋˜๋Š” ๊ณต๊ฐœ ํ”„๋ก์‹œ ํ•„์š”
  • ์„œ๋ฒ„ ๊ฒ€์ƒ‰/DB ์—†์Œ: ๋ชจ๋“  ํ•„ํ„ฐยท๊ฒ€์ƒ‰ยท์ •๋ ฌ์€ ํด๋ผ์ด์–ธํŠธ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ˆ˜ํ–‰(ํ˜„์žฌ 50๊ฐœ ์ œํ’ˆ ๊ทœ๋ชจ์— ์ตœ์ ํ™”)
  • ๋ฐ์ดํ„ฐ ๊ฐฑ์‹  = ํŒŒ์ผ ํŽธ์ง‘: ์ œํ’ˆ ์ถ”๊ฐ€/์ˆ˜์ •์€ JSON ์ง์ ‘ ํŽธ์ง‘ + scripts/ ๋„๊ตฌ(์ˆ˜๋™ ์‹คํ–‰)
  • file:// ์ง์ ‘ ์—ด๊ธฐ ์ œ์•ฝ: fetch()๊ฐ€ ์ฐจ๋‹จ๋˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ๋กœ์ปฌ HTTP ์„œ๋ฒ„๋กœ ์‹คํ–‰

โ˜๏ธ ๋ฐฑ์—”๋“œ/์„œ๋ฒ„๋ฆฌ์Šค API๊ฐ€ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ

๊ธฐ๋Šฅ ์ƒํƒœ
RSS ์ˆ˜์ง‘ยทํŒŒ์‹ฑยท์บ์‹œ ๊ฐฑ์‹  โœ… ๊ตฌํ˜„๋จ โ€” serverless/worker.js (Cloudflare Worker, ์„ ํƒ ๋ฐฐํฌ). ๋ฏธ๋ฐฐํฌ ์‹œ ๊ณต๊ฐœ ํ”„๋ก์‹œ ํด๋ฐฑ
๋งค๋‰ด์–ผ ID ์ž๋™ ํ•ด์„(๊ณต์‹ ์‚ฌ์ดํŠธ ์Šคํฌ๋ž˜ํ•‘) โŒ ๋ฏธ๊ตฌํ˜„ โ€” ํ˜„์žฌ๋Š” ๊ณต์‹ ๋งค๋‰ด์–ผ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋งํฌ๋กœ ๋Œ€์ฒด
์ด๋ฏธ์ง€ ํ”„๋ก์‹œ/๋ฐฑ์—…(์™ธ๋ถ€ ์ด๋ฏธ์ง€ ํ˜ธ์ŠคํŠธ ์žฅ์•  ๋Œ€๋น„) โŒ ๋ฏธ๊ตฌํ˜„ โ€” ์‹คํŒจ ์‹œ ๋กœ์ปฌ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ํ‘œ์‹œ๋งŒ
๊ฐ€๊ฒฉ/์žฌ๊ณ  ๋“ฑ ์™ธ๋ถ€ ์‹ค๋ฐ์ดํ„ฐ ์—ฐ๋™ โŒ ๋ฏธ๊ตฌํ˜„ โ€” ๋ฐ์ดํ„ฐ๋Š” ์ •์  JSON

์•„ํ‚คํ…์ฒ˜: ๋ฐฑ์—”๋“œ์„ฑ ๊ธฐ๋Šฅ ๋ถ„๋ฆฌ

๊ธฐ๋Šฅ ๊ณ„์ธต ์œ„์น˜
RSS ์ˆ˜์ง‘ยทํŒŒ์‹ฑยท์บ์‹œ ๊ฐฑ์‹  ์„œ๋ฒ„๋ฆฌ์Šค API (๋Ÿฐํƒ€์ž„) serverless/worker.js โ€” GET /api/news
๋ฐ˜๋‹ค์ด ๋งค๋‰ด์–ผ ๊ฒ€์ƒ‰ ๊ณต์‹ ์‚ฌ์ดํŠธ ์•„์›ƒ๋ฐ”์šด๋“œ ๋งํฌ js/app.js
๋ฐ์ดํ„ฐ ๊ฒ€์ฆยทID ๋งคํ•‘ ๋นŒ๋“œ ํƒ€์ž„ ์Šคํฌ๋ฆฝํŠธ scripts/

์„œ๋ฒ„๋ฆฌ์Šค API ๋ฐฐํฌ (์„ ํƒยท๊ถŒ์žฅ)

cd serverless
npx wrangler deploy   # Cloudflare ๊ณ„์ • ํ•„์š” (๋ฌด๋ฃŒ ํ”Œ๋žœ ๊ฐ€๋Šฅ)

๋ฐฐํฌ ํ›„ ๋ฐœ๊ธ‰ URL์„ js/notifications.js์˜ API_BASE์— ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ HTML์—์„œ window.GUNPLA_API_BASE๋กœ ์ง€์ •ํ•˜์„ธ์š”. ๋ฏธ์„ค์ • ์‹œ ์ž๋™์œผ๋กœ ํ”„๋ก์‹œ ํด๋ฐฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ธ: serverless/README.md

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

GunList/
โ”œโ”€โ”€ index.html              # ๋ฉ”์ธ (๋ชฉ๋ก/ํ•„ํ„ฐ/์ฆ๊ฒจ์ฐพ๊ธฐ/๋น„๊ต/์•Œ๋ฆผ)
โ”œโ”€โ”€ detail.html             # ์ œํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€
โ”œโ”€โ”€ css/
โ”‚   โ”œโ”€โ”€ styles-base.css     # ํ† ํฐ, ๋ ˆ์ด์•„์›ƒ, ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ, ์ •์ œ ๋ ˆ์ด์–ด
โ”‚   โ”œโ”€โ”€ styles-components.css # ์นด๋“œ/ํ•„ํ„ฐ ๋“ฑ ์ปดํฌ๋„ŒํŠธ
โ”‚   โ””โ”€โ”€ styles-themes.css   # ํ…Œ๋งˆ๋ณ„ ๋น„์ฃผ์–ผ ์•„์ด๋ดํ‹ฐํ‹ฐ(๊ทธ๋ผ๋ฐ์ด์…˜ยท์‹ฌ๋ณผ)
โ”œโ”€โ”€ js/
โ”‚   โ”œโ”€โ”€ app.js              # ์•ฑ ์ฝ”์–ด(๋ Œ๋”/์ฆ๊ฒจ์ฐพ๊ธฐ/๋น„๊ต/์ตœ๊ทผ์กฐํšŒ)
โ”‚   โ”œโ”€โ”€ filter.js           # ํ•„ํ„ฐยท๊ฒ€์ƒ‰ยท์ž๋™์™„์„ฑยทURL ์ƒํƒœ
โ”‚   โ”œโ”€โ”€ recommendation.js   # ์ถ”์ฒœ ์ ์ˆ˜ ๋กœ์ง
โ”‚   โ”œโ”€โ”€ i18n.js             # ๋‹ค๊ตญ์–ด + ํ…Œ๋งˆ ๊ด€๋ฆฌ
โ”‚   โ””โ”€โ”€ notifications.js    # RSS ์†Œ์‹ ํŒจ๋„ (API ์šฐ์„  + ํ”„๋ก์‹œ ํด๋ฐฑ)
โ”œโ”€โ”€ data/
โ”‚   โ”œโ”€โ”€ gunpla-index.json   # ์ œํ’ˆ ๋ชฉ๋ก (์Šคํ‚ค๋งˆ: ์•„๋ž˜ ์ฐธ๊ณ )
โ”‚   โ”œโ”€โ”€ gunpla-details/     # ์ œํ’ˆ๋ณ„ ์ƒ์„ธ JSON
โ”‚   โ”œโ”€โ”€ taxonomy.json       # ๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ/๋ผ๋ฒจ(koยทen)
โ”‚   โ””โ”€โ”€ i18n.json           # UI ๋ฒˆ์—ญ
โ”œโ”€โ”€ scripts/                # ๋ฐ์ดํ„ฐ ์œ ์ง€๋ณด์ˆ˜ ์Šคํฌ๋ฆฝํŠธ (๋นŒ๋“œ ํƒ€์ž„, Node ํ•„์š”)
โ””โ”€โ”€ serverless/             # ์„œ๋ฒ„๋ฆฌ์Šค API ๊ณ„์ธต โ€” RSS ์ˆ˜์ง‘ยท์บ์‹œ (Cloudflare Worker)

์‹คํ–‰ ๋ฐฉ๋ฒ•

์ •์  ์‚ฌ์ดํŠธ์ด๋ฏ€๋กœ ๋กœ์ปฌ HTTP ์„œ๋ฒ„๋กœ ์—ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (file:// ์ง์ ‘ ์—ด๊ธฐ๋Š” fetch ์ฐจ๋‹จ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์Œ).

# ๋ฐฉ๋ฒ• 1: Python ๋‚ด์žฅ ์„œ๋ฒ„
python -m http.server 8000

# ๋ฐฉ๋ฒ• 2: Node
npx serve .

# ๋ฐฉ๋ฒ• 3: VS Code "Live Server" ํ™•์žฅ โ†’ index.html ์šฐํด๋ฆญ โ†’ Open with Live Server

์ดํ›„ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:8000 ์ ‘์†.

ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•

์ž๋™ํ™” ํ…Œ์ŠคํŠธ๋Š” ์•„์ง ์—†์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ํ›„ ์•„๋ž˜ ์ˆ˜๋™ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋กœ ๊ฒ€์ฆํ•˜์„ธ์š”.

์ˆ˜๋™ ๊ฒ€์ฆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  1. ํƒ์ƒ‰: ํ•„ํ„ฐ 2~3๊ฐœ ์„ ํƒ โ†’ ๊ฒฐ๊ณผ ์ˆ˜ ๋ณ€ํ™”, ์ ์ˆ˜์ˆœ ์ •๋ ฌ ํ™•์ธ / ๊ฒ€์ƒ‰(์ดˆ์„ฑ ํฌํ•จ) / ์ •๋ ฌยท๋ทฐ ์ „ํ™˜
  2. ์ฆ๊ฒจ์ฐพ๊ธฐ/๋น„๊ต: ์นด๋“œยท์ƒ์„ธ์—์„œ ํ† ๊ธ€ โ†’ ๋ฐฐ์ง€ยท๋ฒ„ํŠผ ์ƒํƒœ / ๋น„๊ตํ•จ ํƒญ์—์„œ ํ…Œ์ด๋ธ” ํ‘œ์‹œยท์—ด ์ œ๊ฑฐ / ํƒญ ๋‘ ๊ฐœ ์—ด๊ณ  ํฌ๋กœ์Šคํƒญ ๋™๊ธฐํ™”
  3. ํ…Œ๋งˆ/์–ธ์–ด: 8์ข… ํ…Œ๋งˆ ์ˆœํšŒ + ์ปค์Šคํ…€ ์ƒ‰์ƒ ๋ณ€๊ฒฝ โ†’ ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์œ ์ง€ / KOโ†”EN ์ „ํ™˜ ์‹œ ์ „ ์˜์—ญ ๊ฐฑ์‹ 
  4. ์•Œ๋ฆผ: ๋ฒจ ํด๋ฆญ โ†’ ๋ชฉ๋ก/์ฝ์Œ ์ฒ˜๋ฆฌ, โ†ป ์ƒˆ๋กœ๊ณ ์นจ (๋„คํŠธ์›Œํฌยทํ”„๋ก์‹œ ์ƒํƒœ์— ๋”ฐ๋ผ ์‹คํŒจ ๊ฐ€๋Šฅ โ€” ์บ์‹œ ํด๋ฐฑ ํ™•์ธ)
  5. ์ƒํƒœ ์œ ์ง€: ์ƒˆ๋กœ๊ณ ์นจยท์ƒ์„ธโ†’ํ™ˆ ๋ณต๊ท€ ํ›„ ์ฆ๊ฒจ์ฐพ๊ธฐ/๋น„๊ต/์ตœ๊ทผ์กฐํšŒ ์œ ์ง€
  6. ๋ฐ˜์‘ํ˜•: 375px/768px/1280px์—์„œ ํ—ค๋”ยท์นด๋“œยท๋น„๊ต ํ…Œ์ด๋ธ” ๊ฒน์นจ ์—†์Œ

์„œ๋ฒ„๋ฆฌ์Šค API ๋กœ์ปฌ ํ…Œ์ŠคํŠธ (Node ํ•„์š”)

cd serverless && npx wrangler dev
curl http://127.0.0.1:8787/api/health
curl "http://127.0.0.1:8787/api/news?limit=5"

๋ฐ์ดํ„ฐ ์Šคํฌ๋ฆฝํŠธ (Node ํ•„์š”)

scripts/apply-bandai-ids.js, scripts/update-model-numbers.js โ€” ์‹คํ–‰ ์ „ ๊ฐ ํŒŒ์ผ ์ƒ๋‹จ ์ฃผ์„ ์ฐธ๊ณ .

๋ฐ์ดํ„ฐ ์Šคํ‚ค๋งˆ

data/gunpla-index.json

{
  "meta": { "version": "1.0.0", "totalCount": 50, "lastUpdated": "YYYY-MM-DD" },
  "products": [{
    "id": "hg-rx-78-2-revive",        // ๊ณ ์œ  ์Šฌ๋Ÿฌ๊ทธ (์ƒ์„ธ JSON ํŒŒ์ผ๋ช…๊ณผ ์ผ์น˜)
    "baseProductId": "rx-78-2",       // ๋™์ผ ๊ธฐ์ฒด ๋ณ€ํ˜• ๋ฌถ์Œ ํ‚ค
    "name": { "ko": "...", "en": "..." },
    "modelNumber": "RX-78-2",
    "grade": "HG", "scale": "1/144", "series": "first_gundam",  // taxonomy ์˜ต์…˜ value
    "releaseYear": 2015, "releaseLine": "standard",
    "isRevive": true, "isVerKa": false,
    "thumbnail": "https://gunpla.fyi/images/boxarts/196",       // ์™ธ๋ถ€ ์ด๋ฏธ์ง€ (๋งค๋‰ด์–ผ ID ์•„๋‹˜)
    "price": 1100,                    // ์—”ํ™” ์ •์ˆ˜
    "height": "์•ฝ 130mm",             // ํ‘œ์‹œ์šฉ ๋ฌธ์ž์—ด (์ˆซ์ž ์•„๋‹˜ ์ฃผ์˜)
    "tags": ["beginner", "iconic"],
    "filterData": {                   // ํ•„ํ„ฐ/๋น„๊ต์— ์‚ฌ์šฉ
      "difficulty": "beginner",       // beginner|intermediate|advanced
      "mobility": 4,                  // 1~5
      "frameType": "partial", "partCount": 138, "runnerCount": 7,
      "runnerColors": "4+", "weaponCount": "standard",
      "sealDependency": "partial", "clearParts": "none",
      "coatingParts": false, "transformation": false,
      "colorSeparation": "high", "sizeFeeling": "normal",
      "recommendedUser": ["beginner", "posing"]
    }
  }]
}

data/gunpla-details/{id}.json

{
  // index์™€ ๊ณตํ†ต: id, baseProductId, name, modelNumber, series, grade, scale,
  // releaseYear, releaseLine, isRevive, isVerKa, price, height
  "pilot": "์•„๋ฌด๋กœ ๋ ˆ์ด", "manufacturer": "์ง€๊ตฌ์—ฐ๋ฐฉ๊ตฐ",
  "releaseMonth": 3,
  "images": { "boxart": "https://...", "gallery": [] },
  "fullSpecs": { /* filterData์™€ ์œ ์‚ฌ ํ•„๋“œ โ€” โš ๏ธ ํ•„๋“œ๋ช… ํ˜ผ์šฉ ์žˆ์Œ(ํ‘œ์ค€ํ™” ์˜ˆ์ •) */ },
  "weapons": ["๋น” ๋ผ์ดํ”Œ"], "accessories": [],
  "pros": ["..."], "cons": ["..."],
  "recommendation": "...", "buildingTips": "...",
  "lastUpdated": "YYYY-MM-DD"
}

์ƒ์„ธ JSON์ด ์—†๋Š” ์ œํ’ˆ์€ index ๋ฐ์ดํ„ฐ๋กœ ํด๋ฐฑ ๋ Œ๋”๋ฉ๋‹ˆ๋‹ค.

data/taxonomy.json

{
  "version": "...", "lastUpdated": "...",
  "categories": [{
    "id": "grade",
    "label": { "ko": "๋“ฑ๊ธ‰ (Grade)", "en": "Grade" },
    "type": "single",            // single | multiple | range | boolean
    "options": [{ "value": "HG", "label": { "ko": "...", "en": "..." } }],
    // type=range ์นดํ…Œ๊ณ ๋ฆฌ(releaseYearยทmobilityยทpartCountยทrunnerCount)๋Š”
    // options ๋Œ€์‹  min / max / step ์‚ฌ์šฉ
  }]
}

data/i18n.json

{ "translations": { "ko": { "nav": {...}, "search": {...}, "compare": {...}, "notif": {...}, "footer": {...}, ... },
                    "en": { /* ๋™์ผ ํ‚ค ๊ตฌ์กฐ */ } } }

HTML์—์„œ data-i18n="ํ‚ค.๊ฒฝ๋กœ" / data-i18n-placeholder๋กœ ๋ฐ”์ธ๋”ฉ๋ฉ๋‹ˆ๋‹ค.

localStorage ํ‚ค

ํ‚ค ์šฉ๋„
gunpla-lang ์–ธ์–ด(ko/en)
gunpla-theme ํ…Œ๋งˆ ์ด๋ฆ„
gunpla-theme-custom ์ปค์Šคํ…€ ํ…Œ๋งˆ ์ƒ‰์ƒ JSON
gunpla-favorites ์ฆ๊ฒจ์ฐพ๊ธฐ ์ œํ’ˆ id ๋ฐฐ์—ด
gunpla-compare ๋น„๊ตํ•จ ์ œํ’ˆ id ๋ฐฐ์—ด(์ตœ๋Œ€ 4)
gunpla-recent-viewed ์ตœ๊ทผ ์กฐํšŒ id ๋ฐฐ์—ด(์ตœ๋Œ€ 10)
gunpla-search-history ๊ฒ€์ƒ‰ ๊ธฐ๋ก
gunpla-news-cache ์†Œ์‹ ์บ์‹œ {ts, items} (TTL 30๋ถ„)
gunpla-news-seen ๋งˆ์ง€๋ง‰ ์ฝ์Œ ์‹œ๊ฐ(ms)

๋ผ์ด์„ ์Šค

์ด ํ”„๋กœ์ ํŠธ๋Š” ํŒฌ ์ œ์ž‘ ๋น„๊ณต์‹ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ๊ฑดํ”„๋ผ ๋ฐ ๊ด€๋ จ ์ƒํ‘œ๋Š” BANDAI SPIRITS์˜ ๋“ฑ๋ก์ƒํ‘œ์ž…๋‹ˆ๋‹ค.


Made with โค๏ธ for Gunpla enthusiasts

About

Gunpla Decision Support System (aka. gunpla finder)

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages