๐ŸŒŸ React 19 + Vite + p5.js ํ”„๋กœ์ ํŠธ ์…‹์—…

BamgasiJMยท2025๋…„ 11์›” 1์ผ

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
4/67
post-thumbnail

๋“ค์–ด๊ฐ€๋ฉฐ

ํฌ๋ฆฌ์—์ดํ‹ฐ๋ธŒ ์ฝ”๋”ฉ๊ณผ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์•„ํŠธ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ p5.js๋Š” ์—ฌ์ „ํžˆ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. React ์ƒํƒœ๊ณ„์™€ ๊ฒฐํ•ฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜์˜ ๊ตฌ์กฐํ™”๋œ ํ”„๋กœ์ ํŠธ ์•ˆ์—์„œ ๋น„์ฃผ์–ผ ์ฝ”๋”ฉ์„ ์ž์œ ๋กญ๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์ฃ .

๊ณผ๊ฑฐ์—๋Š” react-p5 ๊ฐ™์€ ์„œ๋“œํŒŒํ‹ฐ ๋ž˜ํผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์ง€๋งŒ, 2026๋…„ ํ˜„์žฌ React์˜ ํ›…(useRef, useEffect)๋งŒ์œผ๋กœ๋„ p5.js๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค ์™ธ๋ถ€ ๋ž˜ํผ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ํŽธ์ด ๋” ๊ฐ€๋ณ๊ณ , ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ, React์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๋Š” ๋ฐ์—๋„ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” React 19 + Vite 8 + p5.js v2 ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•˜์—ฌ, ์™ธ๋ถ€ ๋ž˜ํผ ์—†์ด React ํ›…๋งŒ์œผ๋กœ p5.js ์Šค์ผ€์น˜๋ฅผ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋‹ค๋ฃน๋‹ˆ๋‹ค.


1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

Create React App(CRA)์€ ๊ณต์‹ ์ง€์›์ด ์ค‘๋‹จ๋˜์—ˆ์œผ๋ฏ€๋กœ, ํ˜„์žฌ React ์ƒํƒœ๊ณ„์˜ ํ‘œ์ค€ ๋นŒ๋“œ ๋„๊ตฌ์ธ Vite๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

npm create vite@latest my-p5-app

์‹คํ–‰ ํ›„ ๋‚˜ํƒ€๋‚˜๋Š” ํ”„๋กฌํ”„ํŠธ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

  • Framework: React
  • Variant: JavaScript

์ดํ›„ ํ”„๋กœ์ ํŠธ ํด๋”๋กœ ์ด๋™ํ•ด ๊ธฐ๋ณธ ์˜์กด์„ฑ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

cd my-p5-app
npm install

2. p5.js ์˜์กด์„ฑ ์„ค์น˜

์™ธ๋ถ€ ๋ž˜ํผ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  p5.js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์ง์ ‘ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

npm install p5

๐Ÿ’ก ์™œ ๋ž˜ํผ๋ฅผ ์“ฐ์ง€ ์•Š์„๊นŒ?
@p5-wrapper/react ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์กด์žฌํ•˜์ง€๋งŒ, p5.js๋Š” ์ž์ฒด์ ์œผ๋กœ ์ธ์Šคํ„ด์Šค ๋ชจ๋“œ(Instance Mode) ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. React์˜ useRef๋กœ ์บ”๋ฒ„์Šค๊ฐ€ ๋งˆ์šดํŠธ๋  DOM์„ ์žก๊ณ , useEffect๋กœ ์ธ์Šคํ„ด์Šค์˜ ์ƒ์„ฑ/์ •๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉด ๊ทธ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ์„ ํ•˜๋‚˜๋ผ๋„ ์ค„์ด๋Š” ๊ฒƒ์ด ์žฅ๊ธฐ์ ์ธ ์œ ์ง€๋ณด์ˆ˜์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


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

์ตœ์ข…์ ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

my-p5-app/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ main.jsx           # ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ
โ”‚   โ”œโ”€โ”€ App.jsx            # p5 ์ธ์Šคํ„ด์Šค๋ฅผ ๋งˆ์šดํŠธํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
โ”‚   โ”œโ”€โ”€ index.css          # ์ „์—ญ ์Šคํƒ€์ผ
โ”‚   โ””โ”€โ”€ sketches/
โ”‚       โ””โ”€โ”€ rayCasting.js  # ์‹ค์ œ p5 ์Šค์ผ€์น˜ ๋กœ์ง
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ vite.config.js

sketches/ ํด๋”๋ฅผ ๋”ฐ๋กœ ๋‘์–ด p5 ์Šค์ผ€์น˜ ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ด€๋ฆฌ์— ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.


4. ์ „์—ญ ์Šคํƒ€์ผ ์„ค์ • (index.css)

๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ์—ฌ๋ฐฑ์„ ์ œ๊ฑฐํ•˜๊ณ , ์บ”๋ฒ„์Šค๊ฐ€ ํ™”๋ฉด์„ ๊ฐ€๋“ ์ฑ„์šฐ๋„๋ก ์ตœ์†Œํ•œ์˜ ์Šคํƒ€์ผ๋งŒ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. p5.js ์ฝ”๋“œ์—์„œ ์ฐธ์กฐํ•  CSS ๋ณ€์ˆ˜๋„ ํ•จ๊ป˜ ์„ ์–ธํ•ด๋‘ก๋‹ˆ๋‹ค.

/* ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ์—ฌ๋ฐฑ ์ œ๊ฑฐ & ์Šคํฌ๋กค ๋ฐฉ์ง€ */
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #000;
}

/* p5.js ์ฝ”๋“œ๊ฐ€ ์ฐธ์กฐํ•˜๋Š” --accent ๋ณ€์ˆ˜๋ฅผ ์ „์—ญ์œผ๋กœ ์„ ์–ธ */
:root {
  --accent: #ffffff;
}

๐Ÿ’ก CSS ๋ณ€์ˆ˜์™€ p5.js์˜ ์—ฐ๊ฒฐ
:root์— ์„ ์–ธํ•œ --accent ๊ฐ’์€ getComputedStyle(document.documentElement).getPropertyValue('--accent')๋ฅผ ํ†ตํ•ด p5.js ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒ‰์ƒ ํ…Œ๋งˆ๋ฅผ CSS ํ•œ ๊ณณ์—์„œ๋งŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋งค์šฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.


5. ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ (main.jsx)

Vite๊ฐ€ ์ƒ์„ฑํ•œ ๊ธฐ๋ณธ main.jsx๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. React 19์˜ createRoot API๋กœ ๋ฃจํŠธ DOM์— ์•ฑ์„ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค.

import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; // React ๋ฐ DOM ๋ Œ๋”๋ง ๋ชจ๋“ˆ์„ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import "./index.css";                          // ์ „์—ญ ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import App from "./App.jsx";                   // ๋ฉ”์ธ App ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.

// ๋ฃจํŠธ ์š”์†Œ์— App ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง.
createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

๐Ÿง StrictMode๋Š” ์™œ ๋ถ™์—ฌ์•ผ ํ• ๊นŒ?

<StrictMode>๋Š” ๊ฐœ๋ฐœ ๋ชจ๋“œ์—์„œ๋งŒ ์ž‘๋™ํ•˜๋Š” React์˜ "๋ฒ„๊ทธ ํƒ์ง€๊ธฐ" ์ž…๋‹ˆ๋‹ค.
ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ์—์„œ๋Š” ์•„๋ฌด๋Ÿฐ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ ์„ฑ๋Šฅ ๊ฑฑ์ • ์—†์ด ๋ถ™์—ฌ๋‘๋ฉด ๋ฉ๋‹ˆ๋‹ค.

StrictMode๋Š” ๊ฐœ๋ฐœ ์ค‘์— ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ "๋งˆ์šดํŠธ โ†’ ์–ธ๋งˆ์šดํŠธ โ†’ ๋‹ค์‹œ ๋งˆ์šดํŠธ" ํ•˜๋Š”
๊ณผ์ •์„ ํ•œ ๋ฒˆ ๋” ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” useEffect์˜ ํด๋ฆฐ์—… ๋กœ์ง์ด
์ œ๋Œ€๋กœ ์ž‘์„ฑ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

p5.js์ฒ˜๋Ÿผ ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ค๋ฃจ๋Š” ์ฝ”๋“œ์—์„œ ํŠนํžˆ ์œ ์šฉํ•œ๋ฐ, ๋งŒ์•ฝ p5Instance.remove()
ํด๋ฆฐ์—…์„ ๋นผ๋จน์—ˆ๋‹ค๋ฉด ์บ”๋ฒ„์Šค๊ฐ€ ์ค‘๋ณต ์ƒ์„ฑ๋˜๋Š” ๋ฒ„๊ทธ๋ฅผ ๊ฐœ๋ฐœ ์ค‘์— ๋ฐ”๋กœ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


6. ํ•ต์‹ฌ: React ์ปดํฌ๋„ŒํŠธ์—์„œ p5 ์ธ์Šคํ„ด์Šค ๋งˆ์šดํŠธํ•˜๊ธฐ (App.jsx)

์ด ๊ธ€์˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํŒŒํŠธ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ ๋ž˜ํผ ์—†์ด React์˜ useRef์™€ useEffect๋งŒ์œผ๋กœ p5.js๋ฅผ ์—ฐ๋™ํ•ฉ๋‹ˆ๋‹ค.

import { useEffect, useRef } from "react";
import p5 from "p5";
import rayCastingSketch from "./sketches/rayCasting";

export default function App() {
  // p5 ์บ”๋ฒ„์Šค๊ฐ€ ๋ Œ๋”๋ง๋  DOM ์š”์†Œ๋ฅผ ์ฐธ์กฐํ•˜๊ธฐ ์œ„ํ•œ ref
  const p5ContainerRef = useRef(null);

  useEffect(() => {
    // p5 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (์ธ์Šคํ„ด์Šค ๋ชจ๋“œ)
    // ์ฒซ ๋ฒˆ์งธ ์ธ์ž: ์Šค์ผ€์น˜ ํ•จ์ˆ˜, ๋‘ ๋ฒˆ์งธ ์ธ์ž: ์บ”๋ฒ„์Šค๊ฐ€ ์‚ฝ์ž…๋  DOM ๋…ธ๋“œ
    const p5Instance = new p5((p) => {
      rayCastingSketch(p, 800); // ์Šค์ผ€์น˜ ํ•จ์ˆ˜์— p5 ์ธ์Šคํ„ด์Šค(p)์™€ ํฌ๊ธฐ ์ „๋‹ฌ
    }, p5ContainerRef.current);

    // ํด๋ฆฐ์—… ํ•จ์ˆ˜: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋  ๋•Œ p5 ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€
    return () => {
      p5Instance.remove();
    };
  }, []); // ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ๋น„์›Œ ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ ์‹œ ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ์„ค์ •

  return (
    <div
      ref={p5ContainerRef}
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        height: "100vh",
      }}
    ></div>
  );
}

๐Ÿ”‘ ํ•ต์‹ฌ ํฌ์ธํŠธ 3๊ฐ€์ง€

  1. useRef(null)๋กœ DOM ์ฐธ์กฐ ์ค€๋น„
    p5ContainerRef๋Š” p5.js๊ฐ€ ์บ”๋ฒ„์Šค๋ฅผ ๊ทธ๋ ค๋„ฃ์„ ๋นˆ <div>๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. ์•„์ง ๋งˆ์šดํŠธ ์ „์ด๋ฏ€๋กœ ์ดˆ๊ธฐ๊ฐ’์€ null์ž…๋‹ˆ๋‹ค.

  2. new p5(sketch, container) โ€” ์ธ์Šคํ„ด์Šค ๋ชจ๋“œ
    p5 ์ƒ์„ฑ์ž์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ์Šค์ผ€์น˜ ์ฝœ๋ฐฑ์„, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์บ”๋ฒ„์Šค๋ฅผ ์ฒจ๋ถ€ํ•  DOM ๋…ธ๋“œ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ „์—ญ ์Šค์ฝ”ํ”„๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๊ณ  p5๋ฅผ React ์ปดํฌ๋„ŒํŠธ ์•ˆ์— ์•ˆ์ „ํ•˜๊ฒŒ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  3. return () => p5Instance.remove() โ€” ํด๋ฆฐ์—…
    React์˜ Strict Mode๋‚˜ ๋ผ์šฐํŒ…์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ๋งˆ์šดํŠธ/์–ธ๋งˆ์šดํŠธ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ remove()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ์บ”๋ฒ„์Šค๊ฐ€ ์ค‘๋ณต ์ƒ์„ฑ๋˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ƒˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ํ•œ ์ค„์ด ๋ž˜ํผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด๋„ ์•ˆ์ „ํ•˜๊ฒŒ p5๋ฅผ ๋‹ค๋ฃจ๋Š” ๋น„๊ฒฐ์ž…๋‹ˆ๋‹ค.


7. p5 ์Šค์ผ€์น˜ ํŒŒ์ผ ์ž‘์„ฑ (sketches/rayCasting.js)

์ด์ œ ์‹ค์ œ ๋น„์ฃผ์–ผ ๋กœ์ง์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. p5.js์˜ ์ธ์Šคํ„ด์Šค ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๋ชจ๋“  API๋Š” ์ฝœ๋ฐฑ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜(์—ฌ๊ธฐ์„œ๋Š” p)๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์™œ p5๊ฐ€ ์•„๋‹ˆ๋ผ p์ผ๊นŒ?
p5๋ผ๋Š” ์ด๋ฆ„์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž์ฒด์˜ ์ƒ์„ฑ์ž/๋„ค์ž„์ŠคํŽ˜์ด์Šค์™€ ํ˜ผ๋™๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ ๊ด€๋ก€์ ์œผ๋กœ ์งง์€ p๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ p.setup, p.draw ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” 2D ๋ ˆ์ด ์บ์ŠคํŒ… ์˜ˆ์ œ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

export default function rayCastingSketch(p, size) {
  // --- ๋ณ€์ˆ˜ ๋ฐ ์ƒํƒœ ์„ค์ • ์˜์—ญ ---
  let walls = [];                 // ๊ด‘์„ ์ด ์ถฉ๋Œํ•  ๋ฒฝ(์„ ๋ถ„)๋“ค์„ ๋‹ด๋Š” ๋ฐฐ์—ด
  let accentR, accentG, accentB;  // ๊ฐ•์กฐ ์ƒ‰์ƒ์˜ RGB ์„ฑ๋ถ„
  let light = { x: size / 2, y: size / 2 }; // ๊ด‘์›์˜ ํ˜„์žฌ ์œ„์น˜
  let time = 0;                   // ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ๋ฆ„์„ ์œ„ํ•œ ์‹œ๊ฐ„ ๋ณ€์ˆ˜
  let mouseActive = false;        // ๋งˆ์šฐ์Šค ์ž…๋ ฅ ์—ฌ๋ถ€ ์ƒํƒœ
  const NUM_RAYS = 180;           // ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐœ์‚ฌํ•  ๊ด‘์„ ์˜ ์ˆ˜

  // ============================================================
  // p5.js ์ดˆ๊ธฐํ™”: ์บ”๋ฒ„์Šค ๋ฐ ํ™˜๊ฒฝ ์„ค์ •
  // ============================================================
  p.setup = function () {
    p.createCanvas(size, size); // ์ง€์ •๋œ ํฌ๊ธฐ๋กœ ์บ”๋ฒ„์Šค ์ƒ์„ฑ

    // CSS ๋ณ€์ˆ˜(--accent)์—์„œ ๊ฐ•์กฐ ์ƒ‰์ƒ ๊ฐ’์„ ๊ฐ€์ ธ์˜ด
    const hex = getComputedStyle(document.documentElement)
      .getPropertyValue("--accent")
      .trim();
    const tmp = p.color(hex);    // hex ๊ฐ’์„ p5.color ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
    accentR = p.red(tmp);        // R ์„ฑ๋ถ„ ์ถ”์ถœ
    accentG = p.green(tmp);      // G ์„ฑ๋ถ„ ์ถ”์ถœ
    accentB = p.blue(tmp);       // B ์„ฑ๋ถ„ ์ถ”์ถœ

    buildWalls();                // ๋ฒฝ(๋„ํ˜•) ์ƒ์„ฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ
    p.background(8, 8, 16);      // ์ดˆ๊ธฐ ๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ •
  };

  // ============================================================
  // p5.js ๋ฉ”์ธ ๋ฃจํ”„: ๋ Œ๋”๋ง ๋ฐ ์ธํ„ฐ๋ž™์…˜
  // ============================================================
  p.draw = function () {
    time += 0.008; // ์‹œ๊ฐ„ ํ๋ฆ„ ์—…๋ฐ์ดํŠธ

    const mouseIn = p.mouseX > 0 && p.mouseX < size
                 && p.mouseY > 0 && p.mouseY < size;
    if (mouseIn) {
      light.x = p.mouseX;        // ๋งˆ์šฐ์Šค ์œ„์น˜๋กœ ๊ด‘์› ์ด๋™
      light.y = p.mouseY;
      mouseActive = true;
    } else if (mouseActive) {
      mouseActive = false;
    }
    if (!mouseActive) updateLightAuto(); // ๋งˆ์šฐ์Šค๊ฐ€ ์—†์œผ๋ฉด ์ž๋™ ์ด๋™

    p.background(8, 8, 16);             // ๋ฐฐ๊ฒฝ์ƒ‰ ์ดˆ๊ธฐํ™”
    const critical = collectCriticalAngles(); // ๊ฒฝ๊ณ„ ๊ฐ๋„ ์ˆ˜์ง‘

    // ... (๊ด‘์„  ๋ฐœ์‚ฌ, ๋‹ค๊ฐํ˜• ๋ Œ๋”๋ง ๋กœ์ง) ...
  };

  // ============================================================
  // ํด๋ฆญ ์ด๋ฒคํŠธ: ๋„ํ˜• ํฌ๊ธฐ ๋ณ€๊ฒฝ
  // ============================================================
  p.mousePressed = function () {
    if (p.mouseX >= 0 && p.mouseX < size && p.mouseY >= 0 && p.mouseY < size) {
      // ํด๋ฆญ ์‹œ ๋„ํ˜• ํฌ๊ธฐ ๋žœ๋ค ๋ณ€๊ฒฝ ๋กœ์ง
    }
  };
}

๐ŸŽจ ์Šค์ผ€์น˜ ํŒŒ์ผ ์ž‘์„ฑ ํŒ

  • p.setup, p.draw, p.mousePressed ๋“ฑ ๋ชจ๋“  ์ด๋ฒคํŠธ์™€ ๋ผ์ดํ”„์‚ฌ์ดํด ํ›…์€ p. ์ ‘๋‘์‚ฌ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  • ๋งค๊ฐœ๋ณ€์ˆ˜ size๋ฅผ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›์œผ๋ฉด, React ์ปดํฌ๋„ŒํŠธ ์ชฝ์—์„œ ์บ”๋ฒ„์Šค ํฌ๊ธฐ๋ฅผ ๋™์ ์œผ๋กœ ์ œ์–ดํ•˜๊ธฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.
  • CSS ๋ณ€์ˆ˜๋ฅผ ์ƒ‰์ƒ ์†Œ์Šค๋กœ ํ™œ์šฉํ•˜๋ฉด, ๋‹คํฌ ๋ชจ๋“œ/๋ผ์ดํŠธ ๋ชจ๋“œ ์ „ํ™˜์ด๋‚˜ ํ…Œ๋งˆ ๋ณ€๊ฒฝ์„ React/p5 ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด CSS๋งŒ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

8. ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰

๋ชจ๋“  ์ค€๋น„๊ฐ€ ๋๋‚ฌ๋‹ค๋ฉด ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ๋„์›Œ๋ด…๋‹ˆ๋‹ค.

npm run dev

๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:5173์— ์ ‘์†ํ•˜๋ฉด, ํ™”๋ฉด ์ค‘์•™์— p5.js ์บ”๋ฒ„์Šค๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์šฐ์Šค๋ฅผ ์›€์ง์ด๋ฉด ๊ด‘์›์ด ๋”ฐ๋ผ๋‹ค๋‹ˆ๊ณ , ํด๋ฆญํ•˜๋ฉด ๋„ํ˜•์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•˜๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์•„ํŠธ ์ž‘ํ’ˆ์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.


9. ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ

์‹ค์ œ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค.

npm run build

๊ฒฐ๊ณผ๋ฌผ์€ dist/ ํด๋”์— ์ƒ์„ฑ๋˜๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฏธ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

npm run preview

dist/ ํด๋”๋ฅผ Vercel, Netlify, GitHub Pages ๋“ฑ์˜ ์ •์  ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค์— ์—…๋กœ๋“œํ•˜๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.


10. ์™œ ์ด ๋ฐฉ์‹์ด 2026๋…„์— ์ตœ์„ ์ธ๊ฐ€?

ํ•ญ๋ชฉ์™ธ๋ถ€ ๋ž˜ํผ ์‚ฌ์šฉuseRef + useEffect ์ง์ ‘ ๊ตฌํ˜„
์˜์กด์„ฑ ์ˆ˜๋งŽ์Œ (๋ž˜ํผ + p5)p5 ํ•˜๋‚˜๋งŒ
React ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ ๋Œ€์‘๋ž˜ํผ ์ธก ์—…๋ฐ์ดํŠธ ๋Œ€๊ธฐ ํ•„์š”์ฆ‰์‹œ ๋Œ€์‘ ๊ฐ€๋Šฅ
์„ธ๋ฐ€ํ•œ ์ œ์–ด์ œํ•œ์ ์™„๋ฒฝํ•œ ์ œ์–ด
๋ผ์ดํ”„์‚ฌ์ดํด ์ดํ•ด๋ธ”๋ž™๋ฐ•์Šค๋ช…์‹œ์  ์ดํ•ด
ํ•™์Šต ๊ฐ€์น˜๋‚ฎ์ŒReact ํ›…๊ณผ p5 ์ธ์Šคํ„ด์Šค ๋ชจ๋“œ๋ฅผ ๋™์‹œ์— ํ•™์Šต

์™ธ๋ถ€ ๋ž˜ํผ๋Š” ํŽธ๋ฆฌํ•จ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๊ฒฐ๊ตญ ๋‚ด๋ถ€์—์„œ๋Š” ๋˜‘๊ฐ™์ด useRef์™€ useEffect๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด ๋ ˆ์ด์–ด ํ•˜๋‚˜๋ฅผ ์ค„์ด๋ฉด์„œ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ , React์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์— ๋Œ€ํ•œ ์ดํ•ด๋„ ๊นŠ์–ด์ง‘๋‹ˆ๋‹ค.


๋งˆ๋ฌด๋ฆฌ

์ด์ œ React 19 + Vite 8 + p5.js v2 ํ™˜๊ฒฝ์—์„œ ์™ธ๋ถ€ ๋ž˜ํผ ์—†์ด React ํ›…๋งŒ์œผ๋กœ p5.js ์Šค์ผ€์น˜๋ฅผ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ์€ ๋‹จ ์„ธ ์ค„์ž…๋‹ˆ๋‹ค.

const p5ContainerRef = useRef(null);
const p5Instance = new p5(sketch, p5ContainerRef.current);
return () => p5Instance.remove();

์ด ํŒจํ„ด๋งŒ ๊ธฐ์–ตํ•˜๋ฉด, ์•ž์œผ๋กœ ์–ด๋–ค ๋ณต์žกํ•œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์•„ํŠธ ํ”„๋กœ์ ํŠธ๋“  React ์ปดํฌ๋„ŒํŠธ ์•ˆ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ด์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์˜ ํฌ๋ฆฌ์—์ดํ‹ฐ๋ธŒํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”! ๐ŸŽจ

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€