[๐ŸŒ™๋‹คํฌ ๋ชจ๋“œ - 2๋ถ€] ๋‹คํฌ ๋ชจ๋“œ ๋„์ž… ์„ฑ๊ณต๊ธฐ

Administrator||์กฐํšŒ์ˆ˜ 156


๋‹คํฌ ๋ชจ๋“œ ๋„์ž… ์„ฑ๊ณต๊ธฐ


์‹คํŒจ์˜ ๊ธฐ๋ก, ๋ชจ๋“  ๊ฒƒ์˜ ์‹œ์ž‘์ 

1๋ถ€์˜ ๋งˆ์ง€๋ง‰, ๋‚˜๋Š” '์ „๋žต์  ํ›„ํ‡ด'๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋‹คํฌ ๋ชจ๋“œ ๋„์ž…์„ ์ž ์ • ์ค‘๋‹จํ–ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์˜€์ง€๋งŒ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋ณ€ํ•˜์ง€ ์•Š์•˜๋˜ ๊ธฐ์ดํ•œ ํ˜„์ƒ. ๊ทธ ๋ฏธ์Šคํ„ฐ๋ฆฌ๋Š” ํ’€๋ฆฌ์ง€ ์•Š๋Š” ์ˆ™์ œ์ฒ˜๋Ÿผ ๋จธ๋ฆฟ์†์„ ๋งด๋Œ์•˜๋‹ค.

ํฌ๊ธฐ๋Š” ์•„๋‹ˆ์—ˆ๋‹ค. ๋‹ค๋งŒ, ๋ฌด์ž‘์ • ๋ถ€๋”ชํžˆ๋Š” ๋Œ€์‹  ํ•œ ๊ฑธ์Œ ๋ฌผ๋Ÿฌ์„œ์„œ ๋‚ด๊ฐ€ ๋‚จ๊ธด ์‹คํŒจ์˜ ๊ธฐ๋ก๋“ค์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ฝ์–ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋งˆ์น˜ ์‚ฌ๊ฑด ํ˜„์žฅ์— ๋‚จ๊ฒจ์ง„ ์ฆ๊ฑฐ๋“ค์„ ๋‹ค์‹œ ์‚ดํŽด๋ณด๋Š” ์ˆ˜์‚ฌ๊ด€์ฒ˜๋Ÿผ. "์™œ ์•ˆ ๋˜์ง€?"๋ผ๋Š” ๋ง‰์—ฐํ•œ ํƒ„์‹ ๋Œ€์‹ , "๋‚ด๊ฐ€ ๋†“์น˜๊ณ  ์žˆ๋Š” ๋ช…๋ฐฑํ•œ ๋‹จ์„œ๋Š” ๋ฌด์—‡์ธ๊ฐ€?" ๋ผ๋Š” ์งˆ๋ฌธ์œผ๋กœ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๋ฐ”๊พธ์—ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ์‹คํŒจ์˜ ์žฟ๋”๋ฏธ ์†์—์„œ, ๋‚˜๋Š” ์ด ๊ธฐ๋‚˜๊ธด ๋””๋ฒ„๊น…์„ ๋๋‚ผ ์ฒซ ๋ฒˆ์งธ ์‹ค๋งˆ๋ฆฌ๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

์ƒˆ๋กœ์šด ๋‹จ์„œ, ์ƒˆ๋กœ์šด ์ „๋žต

๋งˆ์Œ์„ ๋น„์šฐ๊ณ  ์›์ ์—์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•œ ๋ฆฌ์„œ์น˜๋Š” ์ด์ „๊ณผ๋Š” ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ์‹คํŒจ๋ฅผ ๊ฒฝํ—˜ํ•œ ๋ˆˆ์œผ๋กœ ๋ณด๋‹ˆ, ๋ฌด์‹ฌ์ฝ” ์ง€๋‚˜์ณค๋˜ ๊ณต์‹ ๋ฌธ์„œ์˜ ๋ฌธ์žฅ๊ณผ ๊ฐœ๋ฐœ ๋ธ”๋กœ๊ทธ์˜ ์ฝ”๋“œ ์กฐ๊ฐ๋“ค์ด ์ƒˆ๋กœ์šด ์˜๋ฏธ๋กœ ๋‹ค๊ฐ€์™”๋‹ค.

๋‹จ์„œ 1: Toast UI์˜ ๋‘ ์–ผ๊ตด Toast UI Editor์˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋‹ค์‹œ ์ฝ๋˜ ์ค‘, theme prop์— ๋Œ€ํ•œ ์„ค๋ช…์ด ๋ˆˆ์— ๋“ค์–ด์™”๋‹ค. ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์‹œ ํ…Œ๋งˆ๋ฅผ ์ง€์ •ํ•˜๋Š” ์—ญํ• . ๊ทธ ์–ด๋””์—๋„ ๋™์ ์œผ๋กœ ํ…Œ๋งˆ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์€ ์—†์—ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์€ ์ด๋ฏธ ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ๊ณ , ๊ทธ๋“ค์˜ ํ•ด๊ฒฐ์ฑ…์€ ํ•œ๊ฒฐ๊ฐ™์•˜๋‹ค. "DOM์— ์ง์ ‘ .toastui-editor-dark ํด๋ž˜์Šค๋ฅผ ์ฃผ์ž…ํ•˜๋ผ." theme prop์€ ํ—ˆ์ƒ์ด์—ˆ๊ณ , ์ง„์งœ๋Š” DOM ์กฐ์ž‘์— ์žˆ์—ˆ๋‹ค.

โ˜…๋‹จ์„œ 2โ˜…: Tailwind v4์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ์ „ํ™˜ Tailwind v4์˜ ๋ฌธ์„œ๋Š” tailwind.config.js์˜ darkMode: 'class'๊ฐ€ ๋” ์ด์ƒ ํ‘œ์ค€์ด ์•„๋‹˜์„ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. ๋Œ€์‹ , CSS ํŒŒ์ผ ์•ˆ์—์„œ ์ง์ ‘ @custom-variant๋ฅผ ์„ ์–ธํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฉ์‹์„ ์ œ์‹œํ–ˆ๋‹ค. ์Šคํƒ€์ผ๋ง์˜ ์ œ์–ด๊ถŒ์ด JavaScript ์„ค์ • ํŒŒ์ผ์—์„œ ์ˆœ์ˆ˜ CSS๋กœ ๋„˜์–ด์˜จ ๊ฒƒ์ด๋‹ค.

๋‹จ์„œ 3: Next.js์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์‹œ๊ฐ„์ฐจ App Router ํ™˜๊ฒฝ์—์„œ์˜ ๋‹คํฌ ๋ชจ๋“œ ๊ตฌํ˜„ ์˜ˆ์ œ๋“ค์€ ํ•˜๋‚˜๊ฐ™์ด suppressHydrationWarning ์†์„ฑ๊ณผ 'Mounted' ์ƒํƒœ ํŒจํ„ด์„ ์–ธ๊ธ‰ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž์˜ ํ…Œ๋งˆ ์„ค์ •์„ ์•Œ์ง€ ๋ชปํ•œ ์ฑ„ ํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆฌ๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ๋’ค๋Šฆ๊ฒŒ localStorage๋ฅผ ํ™•์ธํ•˜์—ฌ ํ…Œ๋งˆ๋ฅผ ์ ์šฉํ•œ๋‹ค. ์ด ์‹œ๊ฐ„์ฐจ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ HTML ๋ถˆ์ผ์น˜, ์ฆ‰ ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ์˜ค๋ฅ˜๋Š” ๋ฐ˜๋“œ์‹œ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ฌธ์ œ์˜€๋‹ค.

์ด ์„ธ ๊ฐ€์ง€ ๋‹จ์„œ๋Š” ์•ˆ๊ฐœ ์†์— ์žˆ๋˜ ๋ฒ”์ธ์˜ ์œค๊ณฝ์„ ๋“œ๋Ÿฌ๋‚ด๊ณ  ์žˆ์—ˆ๋‹ค. ๋ฒ”์ธ์€ '์•Œ ์ˆ˜ ์—†๋Š” ๋นŒ๋“œ ์‹œ์Šคํ…œ'์ด๋ผ๋Š” ๊ฑฐ๋Œ€ํ•œ ์œ ๋ น์ด ์•„๋‹ˆ์—ˆ๋‹ค. ๋ฒ”์ธ์€ ๋ฐ”๋กœ '๋‚ด๊ฐ€ ์ •ํ™•ํžˆ ์•Œ์ง€ ๋ชปํ–ˆ๋˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ตฌ์ฒด์ ์ธ ๋™์ž‘ ๋ฐฉ์‹' ์ด์—ˆ๋‹ค.

๊ฒฌ๊ณ ํ•œ ํ† ๋Œ€ ์Œ“๊ธฐ

์ƒˆ๋กœ์šด ๋‹จ์„œ๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ, ๋‚˜๋Š” 1๋ถ€์™€๋Š” ์ •๋ฐ˜๋Œ€์˜ ์ „๋žต์„ ์„ธ์› ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ Next.js์˜ ๋นŒ๋“œ ์‹œ์Šคํ…œ์— ๋งก๊ธฐ๋Š” 'ํ†ตํ•ฉ'์ด ์•„๋‹Œ, ๊ฐ์ž์˜ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ์ •์˜ํ•˜๊ณ  ์ œ์–ดํ•˜๋Š” '๋ถ„๋ฆฌ'์™€ '์ •๊ตํ•œ ์ œ์–ด' ์ „๋žต์ด์—ˆ๋‹ค.

๋จผ์ €, next-themes๋ฅผ ๋‹ค์‹œ ์„ค์น˜ํ•˜๊ณ  ThemeProvider๋ฅผ ์„ค์ •ํ–ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” defaultTheme="light", enableSystem={false} ์˜ต์…˜์„ ๋ช…์‹œํ•˜์—ฌ, ์˜ค์ง ๋‚˜์˜ ์ฝ”๋“œ๋งŒ์ด ํ…Œ๋งˆ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ†ต์ œ๊ถŒ์„ ๊ฐ€์ ธ์™”๋‹ค.

๋‹ค์Œ์œผ๋กœ, ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ์˜ค๋ฅ˜๋ฅผ ์›์ฒœ์ ์œผ๋กœ ๋ด‰์‡„ํ•˜๊ธฐ ์œ„ํ•ด 'Mounted' ์ƒํƒœ ํŒจํ„ด์„ ๋„์ž…ํ–ˆ๋‹ค. useEffect๋ฅผ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์™„์ „ํžˆ ๋ Œ๋”๋ง๋œ ํ›„์—๋งŒ ํ…Œ๋งˆ ๊ด€๋ จ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

// Mounted ์ƒํƒœ ํŒจํ„ด ์˜ˆ์‹œ const [mounted, setMounted] = useState(false); const { theme } = useTheme(); useEffect(() => { setMounted(true); }, []); // ๋ Œ๋”๋ง ๋ถ€๋ถ„ // theme === 'dark' && mounted ? 'dark-class' : ''

์ด ๊ฒฌ๊ณ ํ•œ ๊ธฐ๋ฐ˜ ์œ„์—, framer-motion์„ ์ด์šฉํ•œ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ† ๊ธ€ ๋ฒ„ํŠผ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์ด ์ž‘์€ ์„ฑ๊ณต์€ ์•ž์œผ๋กœ ๋‚˜์•„๊ฐˆ ๊ธธ์— ๋Œ€ํ•œ ํ™•์‹ ์„ ์ฃผ์—ˆ๋‹ค.

Editor์™€์˜ ์žฌํšŒ, ์ฒซ ๋ฒˆ์งธ ์„ฑ๊ณต

๊ณผ๊ฑฐ์˜ ์‹คํŒจ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด, Editor.tsx ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ์ „๋žต์„ ์ ์šฉํ–ˆ๋‹ค.

  1. wrapperRef ํŒจํ„ด ๋„์ž…: Editor ์ปดํฌ๋„ŒํŠธ๋ฅผ div๋กœ ๊ฐ์‹ธ๊ณ  ref๋ฅผ ์—ฐ๊ฒฐํ–ˆ๋‹ค. Editor์˜ ๋‚ด๋ถ€ API์— ์˜์กดํ•˜๋Š” ๋Œ€์‹ , ์ด wrapper ๋‚ด๋ถ€์—์„œ DOM์„ ํƒ์ƒ‰ํ•˜๋Š” ๋ฐฉ์‹์€ ํ›จ์”ฌ ์•ˆ์ •์ ์ด๋‹ค.
  2. useLayoutEffect๋ฅผ ์ด์šฉํ•œ ํด๋ž˜์Šค ์ฃผ์ž…: theme ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค, wrapper ๋‚ด๋ถ€์—์„œ .toastui-editor-defaultUI ์š”์†Œ๋ฅผ ์ฐพ์•„ .toastui-editor-dark ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ–ˆ๋‹ค.
// Editor.tsx์˜ ํ•ต์‹ฌ ๋กœ์ง useLayoutEffect(() => { const wrapper = wrapperRef.current; if (!wrapper) return; const tuiEl = wrapper.querySelector<HTMLElement>('.toastui-editor-defaultUI'); if (tuiEl) { if (theme === 'dark' && mounted) { tuiEl.classList.add('toastui-editor-dark'); } else { tuiEl.classList.remove('toastui-editor-dark'); } } }, [theme, mounted]);

๊ฒฐ๊ณผ๋Š” ๋†€๋ผ์› ๋‹ค. ํ…Œ๋งˆ ํ† ๊ธ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์ž, ์—๋””ํ„ฐ๋Š” ๊ฑฐ์ง“๋ง์ฒ˜๋Ÿผ ๋‹คํฌ ๋ชจ๋“œ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ์ „ํ™˜๋˜์—ˆ๋‹ค. ํˆด๋ฐ”, ๋ฐฐ๊ฒฝ, ํ…์ŠคํŠธ ์ƒ‰์ƒ๊นŒ์ง€ ๋ชจ๋“  ๊ฒƒ์ด ์ผ์‚ฌ๋ถˆ๋ž€ํ•˜๊ฒŒ ์›€์ง์˜€๋‹ค. ๊ธธ๊ณ  ๊ธธ์—ˆ๋˜ ์‹ธ์›€์˜ ์ฒซ ๋ฒˆ์งธ ์Šน๋ฆฌ์˜€๋‹ค.

์ด์ œ ๋งˆ์ง€๋ง‰ ๊ด€๋ฌธ, Viewer๋งŒ์ด ๋‚จ์•„์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์•„์ง ๋งˆ์ง€๋ง‰ ์‹œ๋ จ์ด ๋‚˜๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ์ง€ ๋ชปํ–ˆ๋‹ค.

์ตœํ›„์˜ ๋‚œ๊ด€: Tailwind prose์™€์˜ CSS ์ „์Ÿ

Editor ์ ์šฉ์— ์„ฑ๊ณตํ•˜๋ฉฐ ์Šน๋ฆฌ๋ฅผ ์˜ˆ๊ฐํ–ˆ์ง€๋งŒ, ๋งˆ์ง€๋ง‰ ๊ด€๋ฌธ์ธ Viewer์—์„œ 1๋ถ€์˜ ์•…๋ชฝ์ด ์žฌํ˜„๋˜์—ˆ๋‹ค. ๋‹คํฌ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•ด๋„ ๋ณธ๋ฌธ ํ…์ŠคํŠธ๊ฐ€ ๊นŒ๋งฃ๊ฒŒ ๋‚จ์•„ ๊ฐ€๋…์„ฑ์ด ์ „ํ˜€ ํ™•๋ณด๋˜์ง€ ์•Š์•˜๋‹ค. ์ฝ”๋“œ ๋ธ”๋ก ์ฃผ๋ณ€์—๋Š” ์›์น˜ ์•Š๋Š” ์ด์ค‘ ํ…Œ๋‘๋ฆฌ๊ฐ€ ์ƒ๊ฒจ ๋””์ž์ธ์„ ํ•ด์น˜๊ณ  ์žˆ์—ˆ๋‹ค.

๋ฌธ์ œ๋Š” ๋” ์ด์ƒ ๋ฏธ๊ถ ์†์— ์žˆ์ง€ ์•Š์•˜๋‹ค. ๋‚˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ผ๋Š” ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ๋ฅผ ๋“ค๊ณ  '๊ณผํ•™ ์ˆ˜์‚ฌ'์— ์ฐฉ์ˆ˜ํ–ˆ๋‹ค. Styles ํƒญ๊ณผ Computed ํƒญ์„ ์˜ค๊ฐ€๋ฉฐ ๋ชจ๋“  CSS ๊ทœ์น™์„ ๋ˆ์งˆ๊ธฐ๊ฒŒ ์ถ”์ ํ•œ ๋์—, ๋งˆ์นจ๋‚ด ์ง„์งœ ๋ฒ”์ธ์˜ ์ •์ฒด๋ฅผ ๋ฐํ˜€๋ƒˆ๋‹ค.

๋ฒ”์ธ์€ Toast UI๋„, Next.js๋„ ์•„๋‹ˆ์—ˆ๋‹ค. ๋ฐ”๋กœ ๋‚ด๊ฐ€ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋˜ Tailwind Typography ํ”Œ๋Ÿฌ๊ทธ์ธ, @tailwindcss/typography์˜ prose ํด๋ž˜์Šค์˜€๋‹ค.

prose๋Š” ๋งˆํฌ๋‹ค์šด ์ฝ˜ํ…์ธ ๋ฅผ ์•„๋ฆ„๋‹ต๊ฒŒ ๊พธ๋ฉฐ์ฃผ๊ธฐ ์œ„ํ•ด ๋งค์šฐ ๊ตฌ์ฒด์ ์ด๊ณ  ๊ฐ•๋ ฅํ•œ ์Šคํƒ€์ผ ๊ทœ์น™๋“ค์„ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๊ทœ์น™๋“ค์ด Toast UI์˜ ๋‹คํฌ ๋ชจ๋“œ ์Šคํƒ€์ผ๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์•„, ๋‚ด ๋ชจ๋“  ๋…ธ๋ ฅ์„ ๋ฎ์–ด์“ฐ๊ณ  ์žˆ์—ˆ๋˜ ๊ฒƒ์ด๋‹ค. ์ด CSS ์šฐ์„ ์ˆœ์œ„(Specificity) ์ „์Ÿ์—์„œ ์ด๊ธฐ์ง€ ๋ชปํ•˜๋ฉด, ๋‹คํฌ ๋ชจ๋“œ๋Š” ์˜์›ํžˆ ๋ฏธ์™„์„ฑ์œผ๋กœ ๋‚จ์„ ํ„ฐ์˜€๋‹ค.

์—ญ์ „์˜ ์ฝ”๋“œ: ๋‹จ ํ•œ ์ค„์˜ CSS๋กœ ์ „์Ÿ์„ ๋๋‚ด๋‹ค

prose์™€์˜ ์ „์Ÿ์—์„œ ์ด๊ธฐ๊ธฐ ์œ„ํ•œ ์ „๋žต์€ ๋ช…ํ™•ํ–ˆ๋‹ค. prose์˜ ๊ทœ์น™๋ณด๋‹ค ๋” ๊ตฌ์ฒด์ ์ธ, ์šฐ๋ฆฌ๋งŒ์˜ ๊ทœ์น™์„ ๋งŒ๋“ค์–ด ์ „์„ธ๋ฅผ ๋’ค์ง‘๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

๋จผ์ €, ์ฝ”๋“œ ๋ธ”๋ก์˜ ์ด์ค‘ ํ…Œ๋‘๋ฆฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด globals.css์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

/* globals.css */ .prose pre { border: none !important; background-color: transparent !important; }

!important ํ‚ค์›Œ๋“œ๋Š” prose๊ฐ€ ์„ค์ •ํ•œ ๋ชจ๋“  border์™€ background๋ฅผ ๊ฐ•์ œ๋กœ ๋ฌด๋ ฅํ™”์‹œ์ผฐ๋‹ค. ์ด๋กœ์จ react-syntax-highlighter์˜ materialDark ํ…Œ๋งˆ์™€ ๋‚ด๊ฐ€ ์ง์ ‘ ๋งŒ๋“  ์ปค์Šคํ…€ ํ…Œ๋‘๋ฆฌ๋งŒ ํ™”๋ฉด์— ๋‚จ๊ฒŒ ๋˜์—ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ, ๊ฐ€์žฅ ์ค‘์š”ํ–ˆ๋˜ ํ…์ŠคํŠธ ๊ฐ€๋…์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ฐจ๋ก€์˜€๋‹ค. prose์™€ Toast UI์˜ ๋‹คํฌ ๋ชจ๋“œ ํด๋ž˜์Šค๋ฅผ ์กฐํ•ฉํ•œ, ๋ˆ„๊ตฌ๋ณด๋‹ค ๊ฐ•๋ ฅํ•œ ์„ ํƒ์ž๋ฅผ ๋งŒ๋“ค์–ด๋ƒˆ๋‹ค.

/* globals.css */ .prose.toastui-editor-dark { --tw-prose-body: theme('colors.gray.300'); --tw-prose-headings: theme('colors.gray.100'); --tw-prose-links: theme('colors.blue.400'); /* ... ๋“ฑ๋“ฑ */ }

์ด ์ฝ”๋“œ๋Š” Tailwind Typography์˜ ๊ณต์‹์ ์ธ ๋‹คํฌ ๋ชจ๋“œ ์˜ค๋ฒ„๋ผ์ด๋“œ ๋ฐฉ์‹์ด๋‹ค. .prose์™€ .toastui-editor-dark ํด๋ž˜์Šค๊ฐ€ ํ•œ ์š”์†Œ์— ๋™์‹œ์— ์กด์žฌํ•  ๋•Œ, prose๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์ƒ‰์ƒ ๋ณ€์ˆ˜๋ฅผ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋‹คํฌ ๋ชจ๋“œ์šฉ ์ƒ‰์ƒ์œผ๋กœ ๊ต์ฒดํ•˜๋ผ๋Š” ๋ช…๋ น์ด๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด CSS ๊ทœ์น™์ด ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก HTML ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ–ˆ๋‹ค. MarkdownViewer๋ฅผ ๊ฐ์‹ธ๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์ธ PostContent๊ฐ€ prose ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ๋‹คํฌ ๋ชจ๋“œ์ผ ๋•Œ toastui-editor-dark ํด๋ž˜์Šค๋ฅผ ๊ฐ–๋„๋ก ์ฑ…์ž„์„ ์œ„์ž„ํ–ˆ๋‹ค.

// PostContent.tsx const articleClassName = ` prose ... ${theme === 'dark' && mounted ? 'toastui-editor-dark' : ''} `; return ( <article className={articleClassName}> <MarkdownViewer ... /> </article> );

์ด ๋งˆ์ง€๋ง‰ ํผ์ฆ ์กฐ๊ฐ์ด ๋งž์ถฐ์ง€๋Š” ์ˆœ๊ฐ„, ๋ชจ๋“  ๊ฒƒ์ด ๋ฐ”๋€Œ์—ˆ๋‹ค.

๋ฒฝ ๋„ˆ๋จธ์˜ ํ’๊ฒฝ

ํ…Œ๋งˆ ํ† ๊ธ€ ๋ฒ„ํŠผ์„ ๋‹ค์‹œ ํด๋ฆญํ–ˆ๋‹ค. ๋‹คํฌ ๋ชจ๋“œ๋กœ ์ „ํ™˜๋œ ํ™”๋ฉด์—๋Š”, ๋‚ด๊ฐ€ ๊ฟˆ๊พธ๋˜ ๋ชจ๋“  ๊ฒƒ์ด ์™„๋ฒฝํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ์—ˆ๋‹ค.

alt text

์–ด๋‘์šด ๋ฐฐ๊ฒฝ ์œ„๋กœ ๋ฐ๊ฒŒ ๋น›๋‚˜๋Š” ํ…์ŠคํŠธ, materialDark ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ๋ช…ํ™•ํ•œ ๊ฒฝ๊ณ„๋ฅผ ๊ฐ€์ง„ ์ฝ”๋“œ ๋ธ”๋ก, ๋ชจ๋“  ๊ฒƒ์ด ์™„๋ฒฝํ•œ ์กฐํ™”๋ฅผ ์ด๋ฃจ๊ณ  ์žˆ์—ˆ๋‹ค. ๊ธธ๊ณ  ํ—˜๋‚œํ–ˆ๋˜ ๋””๋ฒ„๊น… ์—ฌ์ •์˜ ์ข…์ฐฉ์ง€์˜€๋‹ค.

์ด๋ฒˆ ๊ฒฝํ—˜์„ ํ†ตํ•ด ๋‚˜๋Š” ๊ฐ’๋น„์‹ผ ๊ตํ›ˆ์„ ์–ป์—ˆ๋‹ค. '๊ทธ๋ƒฅ ์•ˆ๋œ๋‹ค'๋Š” ๊ฒƒ์€ ์—†์—ˆ๋‹ค. ๋‹จ์ง€ '์•„์ง ๋‚ด๊ฐ€ ๋ชจ๋ฅด๋Š” ๊ฒƒ'์ด ์žˆ์„ ๋ฟ์ด์—ˆ๋‹ค. ๋ฌธ์ œ ํ•ด๊ฒฐ์˜ ํ•ต์‹ฌ์€ ํ™”๋ คํ•œ ๊ธฐ์ˆ ์ด ์•„๋‹Œ, ๊ฐ€์„ค์„ ์„ธ์šฐ๊ณ , ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋กœ ๊ฒ€์ฆํ•˜๊ณ , ์›์ธ์„ ์ง‘์š”ํ•˜๊ฒŒ ํŒŒ๊ณ ๋“œ๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํƒœ๋„์— ์žˆ์—ˆ๋‹ค.

1๋ถ€์˜ ๋งˆ์ง€๋ง‰, ๋‚˜๋Š” ๋ฐ˜๋“œ์‹œ ์ด ๋ฒฝ์„ ๋„˜๊ฒ ๋‹ค๊ณ  ์•ฝ์†ํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋งˆ์นจ๋‚ด, ๊ทธ ์•ฝ์†์„ ์ง€์ผฐ๋‹ค. ๐Ÿ‘Š


์—ํ•„๋กœ๊ทธ: ๋น›์ด ๋‹ฟ์ง€ ์•Š๋˜ ๊ณณ๊นŒ์ง€, ๋‹คํฌ ๋ชจ๋“œ์˜ ์™„์„ฑ

๊ฐ€์žฅ ํฐ ์‚ฐ์ด์—ˆ๋˜ Editor์™€ Viewer๋ฅผ ์ •๋ณตํ•˜๊ณ  ๋‚˜๋‹ˆ, ๋ˆˆ์•ž์— ๋„“์€ ํ‰์›์ด ํŽผ์ณ์ง„ ๊ธฐ๋ถ„์ด์—ˆ๋‹ค. ๊ฐ€์žฅ ๋ณต์žกํ•˜๊ณ  ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋˜ ๋ฌธ์ œ๋“ค์ด ํ•ด๊ฒฐ๋œ ์ง€๊ธˆ, ๋‚จ์€ ์ž‘์—…์€ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌ์ถ•ํ•œ ๊ฒฌ๊ณ ํ•œ ๊ธฐ๋ฐ˜ ์œ„์— ์ผ๊ด€๋œ ๋””์ž์ธ ์›์น™์„ ์ ์šฉํ•˜๋Š”, ์ฆ๊ฑฐ์šด '๋งˆ๋ฌด๋ฆฌ' ๋‹จ๊ณ„์˜€๋‹ค.

์ƒˆ๋กœ์šด '๊ณจ๋“  ๋ฃฐ'์˜ ํƒ„์ƒ

๋ณต์žกํ•œ CSS ์˜ค๋ฒ„๋ผ์ด๋“œ๋‚˜ !important์™€์˜ ์ „์Ÿ์€ ๋๋‚ฌ๋‹ค. ์ด์ œ ์šฐ๋ฆฌ์—๊ฒŒ๋Š” ๋ชจ๋“  ๊ฒƒ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ•๋ ฅํ•œ ๊ทœ์น™, '๊ณจ๋“  ๋ฃฐ'์ด ์ƒ๊ฒผ๋‹ค. ๋ฐ”๋กœ Tailwind CSS์˜ dark: ์ ‘๋‘์‚ฌ์˜€๋‹ค.

  • bg-white๊ฐ€ ํ•„์š”ํ•œ ๊ณณ์—” dark:bg-stone-800์„,
  • text-gray-800์ด ํ•„์š”ํ•œ ๊ณณ์—” dark:text-gray-200์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—….

์ด ๋‹จ์ˆœํ•œ ํŒจํ„ด์˜ ๋ฐ˜๋ณต๋งŒ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ๊ตฌ์„์— ์–ด๋‘ ์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์—ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „๋ฐ˜์œผ๋กœ์˜ ํ™•์žฅ: ์ ์ง„์  ์™„์„ฑ์˜ ์ฆ๊ฑฐ์›€

์šฐ๋ฆฌ๋Š” ์ฒด๊ณ„์ ์ธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ๋‹คํฌ ๋ชจ๋“œ๋ฅผ ์ ์ง„์ ์œผ๋กœ ์ ์šฉํ•ด๋‚˜๊ฐ”๋‹ค.

  • ๊ธ€๋กœ๋ฒŒ UI (Footer, PostCard): ๊ฐ€์žฅ ๋จผ์ € ์‚ฌ์šฉ์ž์™€ ๋งˆ์ฃผํ•˜๋Š” Footer์™€ ํ™ˆํŽ˜์ด์ง€์˜ PostCard๋ถ€ํ„ฐ ์‹œ์ž‘ํ–ˆ๋‹ค. Header์™€ PostContent์—์„œ ํ™•๋ฆฝํ•œ ๋””์ž์ธ ์–ธ์–ด๋ฅผ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜์—ฌ ์ „์ฒด์ ์ธ ํ†ต์ผ์„ฑ์„ ๋งž์ถ”์—ˆ๋‹ค. ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šค์ผˆ๋ ˆํ†ค UI๊นŒ์ง€ ๋””ํ…Œ์ผํ•˜๊ฒŒ ์ฑ™๊ธฐ๋Š” ๊ฒƒ๋„ ์žŠ์ง€ ์•Š์•˜๋‹ค.

alt text

  • ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ ํŽ˜์ด์ง€์˜ ๋‚˜๋จธ์ง€ ์กฐ๊ฐ๋“ค: ์ด๋ฏธ ์™„์„ฑ๋œ ๋ณธ๋ฌธ์„ ์ค‘์‹ฌ์œผ๋กœ, PostHeader, PostAuthorProfile ๋“ฑ ์ฃผ๋ณ€ UI ์š”์†Œ๋“ค์˜ ์ƒ‰์ƒ์„ ์กฐ์ •ํ–ˆ๋‹ค. ์ด ๊ณผ์ •์€ ๋งˆ์น˜ ์ž˜ ์งœ์ธ ํผ์ฆ์˜ ๋งˆ์ง€๋ง‰ ์กฐ๊ฐ๋“ค์„ ๋งž์ถ”๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฆ๊ฑฐ์› ๋‹ค.

  • ๋Œ“๊ธ€ ์‹œ์Šคํ…œ๊ณผ ์ธ์ฆ ํŽ˜์ด์ง€: ์‚ฌ์šฉ์ž์˜ ์ธํ„ฐ๋ž™์…˜์ด ๊ฐ€์žฅ ํ™œ๋ฐœํ•œ ๋Œ“๊ธ€ ์‹œ์Šคํ…œ๊ณผ ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€์—๋„ ๋™์ผํ•œ ๊ทœ์น™์„ ์ ์šฉํ–ˆ๋‹ค. ๋ณต์žกํ•œ ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ์˜€์ง€๋งŒ, '๊ณจ๋“  ๋ฃฐ' ์•ž์—์„œ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ„๋‹จํ–ˆ๋‹ค. ๊ฐ ํผ ์š”์†Œ์™€ ๋ฒ„ํŠผ, ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€์— dark: ์ ‘๋‘์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ๋ชจ๋“  ์ž‘์—…์ด ๋๋‚ฌ๋‹ค.

alt text

alt text

์ตœ์ข… ์ปค์Šคํ„ฐ๋งˆ์ด์ง•: ๋‚˜๋งŒ์˜ ์ƒ‰์„ ์ž…ํžˆ๋‹ค

๋ชจ๋“  ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ๋๋‚œ ํ›„, ๋งˆ์ง€๋ง‰์œผ๋กœ globals.css์™€ ๊ฐ ์ปดํฌ๋„ŒํŠธ์— ํฉ์–ด์ ธ ์žˆ๋˜ ์ƒ‰์ƒ ์ฝ”๋“œ๋“ค์„ ๋‚ด ์ทจํ–ฅ์— ๋งž๊ฒŒ ๋ฏธ์„ธ ์กฐ์ •ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์กŒ๋‹ค. ์ด ๊ณผ์ •์€ ๋งˆ์น˜ ์ž˜ ๋งŒ๋“ค์–ด์ง„ ์ž๋™์ฐจ์˜ ์ƒ‰์ƒ์„ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๊ฑฑ์ • ์—†์ด ์ˆœ์ˆ˜ํ•˜๊ฒŒ ๋ฏธ์ ์ธ ๋ถ€๋ถ„์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ๊ณ„์˜€๋‹ค.

๊ฒฐ๋ก 

๊ธธ๊ณ  ํ—˜๋‚œํ–ˆ๋˜ ๋‹คํฌ ๋ชจ๋“œ ๋„์ž… ์—ฌ์ •์€ ์ด๋ ‡๊ฒŒ ๋ง‰์„ ๋‚ด๋ ธ๋‹ค. ์ˆ˜๋งŽ์€ ์‹คํŒจ์™€ ๋””๋ฒ„๊น… ๋์— ์–ป์€ ๊ฒƒ์€ ๋‹จ์ˆœํžˆ ๊ฒ€์€ ํ™”๋ฉด์ด ์•„๋‹ˆ์—ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ๊นŠ์ด ์ดํ•ดํ•˜๊ณ , CSS์˜ ์ „์Ÿ์—์„œ ์Šน๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์œผ๋ฉฐ, ๋ฌด์—‡๋ณด๋‹ค ์–ด๋–ค ๋ฌธ์ œ๋“  ๊ฒฐ๊ตญ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ž์‹ ๊ฐ์„ ์–ป์—ˆ๋‹ค.

์ด์ œ ๋‚˜์˜ ๋ธ”๋กœ๊ทธ๋Š” ๋‚ฎ์—๋„, ๋ฐค์—๋„ ์‚ฌ์šฉ์ž๋ฅผ ํŽธ์•ˆํ•˜๊ฒŒ ๋งž์ดํ•  ์ค€๋น„๋ฅผ ๋งˆ์ณค๋‹ค.

Administrator
Written by

Administrator

์•ˆ๋…•ํ•˜์„ธ์š”! Deep Dive! ๋ธ”๋กœ๊ทธ ์ œ์ž‘์ž ์ž…๋‹ˆ๋‹ค.

๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...