๐Ÿ—๏ธ GLSL์„ ์™ธ๋ถ€ ํŒŒ์ผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ

BamgasiJMยท2026๋…„ 6์›” 26์ผ

WebGL

๋ชฉ๋ก ๋ณด๊ธฐ
5/14
post-thumbnail

WebGL์—์„œ GLSL ์…ฐ์ด๋” ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ •์˜ํ•  ๋•Œ JavaScript ๋‚ด๋ถ€์— ๋„ฃ์ง€ ์•Š๊ณ  ๋ณ„๋„์˜ ํŒŒ์ผ๋กœ ๋นผ๊ณ  ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ๊ด€๋ฆฌํ•˜๊ธฐ์— ์ข‹์Šต๋‹ˆ๋‹ค. ๋Œ€๊ฐœ ์ด๋ ‡๊ฒŒ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ์ด ์•ˆ์— GLSL ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. shaders ํด๋” ์†์— ๋„ฃ์œผ๋ฉด ์ข‹๊ฒ ๋„ค์š”.

  • vertex.glsl
  • fragment.glsl

์ด๋ ‡๊ฒŒ ๋ณ„๋„ ํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ์—๋””ํ„ฐ์—์„œ Syntax Highlighting์ด ๋˜๊ณ  ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ๋ณด์•ˆ ์ •์ฑ…์— ๋”ฐ๋ผ JS์—์„œ ๋กœ์ปฌ ํŒŒ์ผ(์—ฌ๊ธฐ์„œ๋Š” .glsl ํŒŒ์ผ)์„ ์ง์ ‘ ์ฝ์„ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์— ์ฃผ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

fecth() API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๊ณ , ์ด ํ…์ŠคํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋™์•ˆ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋ฏ€๋กœ async/await ๊ฐ™์€ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

1. ๋ณด์•ˆ๊ณผ fetch() API

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

๋ณด์•ˆ ๋ชจ๋ธ (Same-Origin Policy):

๋ธŒ๋ผ์šฐ์ €๋Š” '๋™์ผ ์ถœ์ฒ˜ ์ •์ฑ…'์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์ฆ‰, ์›น ํŽ˜์ด์ง€๊ฐ€ ์‹คํ–‰ ์ค‘์ธ ์„œ๋ฒ„(์˜ˆ: http://localhost:8080)์™€ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ•˜๋Š” ์„œ๋ฒ„์˜ ์ฃผ์†Œ๊ฐ€ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ํŒŒ์ผ ๊ฒฝ๋กœ(file:///C:/...)๋Š” HTTP ํ”„๋กœํ† ์ฝœ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์›น ํ‘œ์ค€ ๋ณด์•ˆ ๊ทœ์ •์ƒ ์ง์ ‘์ ์ธ JS ์ ‘๊ทผ์ด ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค.

Fetch API์˜ ์—ญํ• :

fetch()๋Š” HTTP/HTTPS ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด ์„œ๋ฒ„์— "์ด ํŒŒ์ผ์„ ์ฃผ์„ธ์š”"๋ผ๊ณ  ์ •์‹์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋งˆ์น˜ ํŽธ์ง€๋ฅผ ์จ์„œ ์šฐ์ฒด๊ตญ(๋ธŒ๋ผ์šฐ์ €)์„ ํ†ตํ•ด ์ƒ๋Œ€๋ฐฉ(์„œ๋ฒ„)์—๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  ๋‹ต์žฅ์„ ๋ฐ›๋Š” ๊ณผ์ •๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋กœ์ปฌ์—์„œ ๊ฐœ๋ฐœ ์ค‘์ด๋ผ๋ฉด, ๋‹จ์ˆœํ•œ ํŒŒ์ผ ์—ด๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋กœ์ปฌ ์›น ์„œ๋ฒ„(Live Server, Python http.server ๋“ฑ)๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํŒŒ์ผ์— '์ฃผ์†Œ(URL)'๋ฅผ ๋ถ€์—ฌํ•ด์•ผ๋งŒ fetch()๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ (Asynchronous Programming)

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

๋™๊ธฐ(Synchronous) vs ๋น„๋™๊ธฐ(Asynchronous):

  • ๋™๊ธฐ: ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•˜๊ณ  ์ปคํ”ผ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ์นด์šดํ„ฐ ์•ž์— ์„œ์„œ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ. (๋‹ค์Œ ์ž‘์—… ์ˆ˜ํ–‰ ๋ถˆ๊ฐ€)
  • ๋น„๋™๊ธฐ: ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•˜๊ณ  ์ง„๋™๋ฒจ์„ ๋ฐ›์€ ๋’ค, ์ž๋ฆฌ๋กœ ๋Œ์•„์™€ ์ฑ…์„ ์ฝ๊ฑฐ๋‚˜ ์Šค๋งˆํŠธํฐ์„ ๋ณด๋Š” ๊ฒƒ. (๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—… ๊ฐ€๋Šฅ)

Promise (์•ฝ์†):

fetch()๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ์ฆ‰์‹œ ๊ฒฐ๊ณผ๊ฐ’์„ ์ฃผ๋Š” ๋Œ€์‹ , "๋ฏธ๋ž˜์— ๋ฐ์ดํ„ฐ๋ฅผ ์ค„๊ฒŒ"๋ผ๋Š” Promise๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ์ฒด๋Š” ํ˜„์žฌ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค: Pending(๋Œ€๊ธฐ), Fulfilled(์ดํ–‰/์„ฑ๊ณต), Rejected(๊ฑฐ๋ถ€/์‹คํŒจ).

Async/Await (๊ฐ€์žฅ ํ˜„๋Œ€์ ์ธ ๋ฌธ๋ฒ•):

Promise๋ฅผ ๋” ์ง๊ด€์ ์œผ๋กœ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ๋ฌธ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • async: ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘๋™ํ•  ๊ฒƒ์ž„์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  • await: Promise๊ฐ€ ํ•ด๊ฒฐ(Fulfilled)๋  ๋•Œ๊นŒ์ง€ ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ž ์‹œ '์ผ์‹œ ์ •์ง€' ์‹œํ‚ต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ „์ฒด ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ•ด๋‹น ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ํ๋ฆ„๋งŒ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๋‹ค๋ฅธ UI ์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‚˜ ์ด๋ฒคํŠธ๋Š” ์ •์ƒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

3. ์…ฐ์ด๋” ๋กœ๋”ฉ ์ฝ”๋“œ์˜ ๊ตฌ์กฐ

์…ฐ์ด๋” ํŒŒ์ผ์„ fetch()๋กœ ๋ถˆ๋Ÿฌ์™€ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ๋ณธ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

/**
 * @description ์™ธ๋ถ€ ์…ฐ์ด๋” ํŒŒ์ผ์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜
 * @param {string} url - ์…ฐ์ด๋” ํŒŒ์ผ์˜ ๊ฒฝ๋กœ (URL)
 * @returns {Promise<string>} - ์…ฐ์ด๋” ์†Œ์Šค ์ฝ”๋“œ ๋ฌธ์ž์—ด
 */
async function loadShaderSource(url) {
  try {
    // 1. fetch()๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์— ๋ฆฌ์†Œ์Šค ์š”์ฒญ (๋น„๋™๊ธฐ ๋Œ€๊ธฐ)
    const response = await fetch(url);

    // 2. ์‘๋‹ต์ด ์„ฑ๊ณต์ (status 200-299)์ธ์ง€ ํ™•์ธ
    if (!response.ok) {
      throw new Error(`ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค: ${response.statusText}`);
    }

    // 3. ์‘๋‹ต ๋ณธ๋ฌธ์„ ํ…์ŠคํŠธ ํ˜•์‹์œผ๋กœ ์ฝ๊ธฐ (๋น„๋™๊ธฐ ๋Œ€๊ธฐ)
    const shaderCode = await response.text();
    
    console.log("์…ฐ์ด๋” ๋กœ๋“œ ์™„๋ฃŒ!");
    return shaderCode;

  } catch (error) {
    // ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋‚˜ ํŒŒ์ผ ๋ถ€์žฌ ์‹œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    console.error("์…ฐ์ด๋” ๋กœ๋”ฉ ์—๋Ÿฌ:", error);
    return null;
  }
}

// ์‹คํ–‰ ์˜ˆ์‹œ
async function initWebGL() {
  console.log("์ดˆ๊ธฐํ™” ์‹œ์ž‘...");
  
  // ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฐ ๊ฒฐ๊ณผ ๋Œ€๊ธฐ
  const vertexShaderSource = await loadShaderSource('./shaders/vertex.glsl');
  const fragmentShaderSource = await loadShaderSource('./shaders/fragment.glsl');

  if (vertexShaderSource && fragmentShaderSource) {
    // ์…ฐ์ด๋” ์ปดํŒŒ์ผ ๋ฐ WebGL ํ”„๋กœ๊ทธ๋žจ ์ƒ์„ฑ ๋‹จ๊ณ„๋กœ ์ง„์ž…
    console.log("์…ฐ์ด๋” ์†Œ์Šค ์ค€๋น„ ์™„๋ฃŒ. ์ปดํŒŒ์ผ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.");
  } else {
    console.error("์…ฐ์ด๋” ์†Œ์Šค๊ฐ€ ์—†์–ด ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
  }
}

initWebGL();

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

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