1.
Q1: OOP는 Stateful 프로그램에는 적합하지만, Stateless 프로그램에는 적합도가 떨어지는 편인가?
A1: ① Stateful과 Stateless의 개념
Stateful 프로그램은 내부에 상태(state)를 유지하며, 그 상태에 따라 프로그램의 동작이 달라집니다. 예를 들어, 은행 계좌의 잔액 정보를 저장하고 그에 따라 입출금 작업을 수행하는 시스템은 상태를 유지해야 합니다.
반면에 Stateless 프로그램은 내부에 상태를 유지하지 않습니다. 즉, 각 요청이나 함수 호출은 이전의 상태에 영향을 받지 않고 독립적으로 처리됩니다. 예를 들어, 단순한 계산기 프로그램은 입력값에 따라 결과를 반환하지만, 내부에 상태를 저장하지 않습니다.
A1: ② 객체지향 프로그래밍(OOP)과 Stateful 프로그램
객체지향 프로그래밍(OOP)은 데이터와 해당 데이터를 조작하는 메서드를 하나의 객체로 묶어 처리합니다.
이 객체들은 상태를 보유하고 있으며, 그 상태에 따라 동작이 달라집니다. 따라서 OOP는 상태를 관리하고 조작하는 데에 강점이 있어 Stateful 프로그램에 적합합니다.
하지만 Stateless 프로그램에서는 상태 관리가 중요하지 않기 때문에, OOP의 장점이 크게 부각되지 않을 수 있습니다. 이 경우, 함수형 프로그래밍이나 절차지향 프로그래밍이 더 적합할 수 있습니다.
2.
Q2: 프레임웍의 도움, 언어 자체의 모듈화 지원은 OOP의 장점을 희석하는가?
A2: ① 현대 프로그래밍의 변화
현대 프로그래밍에서는 다음과 같은 변화가 있습니다:
- 프레임워크의 활용: 개발자들은 바닥부터 모든 코드를 작성하지 않고, 이미 검증된 프레임워크를 사용하여 생산성을 높입니다. 예를 들어, React, Angular 같은 프레임워크를 사용하면 많은 구조가 이미 정의되어 있습니다.
- 언어의 발전: 최신 언어들은 타입 시스템과 모듈화를 지원하여 코드의 안정성과 재사용성을 높입니다. 이는 전통적인 OOP의 일부 장점을 언어 자체에서 제공한다는 의미입니다.
이러한 변화로 인해, OOP의 필요성이 감소했다는 의견이 있을 수 있습니다. 그러나 이는 OOP의 장점이 사라졌다기보다는, 다른 패러다임과 혼합되어 사용된다는 의미로 해석할 수 있습니다.
A2: ② JavaScript와 객체지향 프로그래밍
JavaScript는 원래 프로토타입 기반 언어로 시작했지만, ES6부터는 class 문법이 도입되어 OOP 스타일의 코딩이 가능해졌습니다. 하지만 JavaScript에서의 클래스는 전통적인 OOP 언어의 클래스와는 다소 차이가 있습니다.
- 클래스 문법: 편의성을 위해 제공되지만, 실제로는 프로토타입 기반 상속을 사용합니다.
- 함수형 프로그래밍 패러다임: JavaScript는 일급 함수, 클로저 등 함수형 프로그래밍의 특징도 강하게 가지고 있습니다.
React도 함수형 컴포넌트와 훅(Hooks)을 도입하면서 함수형 프로그래밍 패러다임을 적극적으로 활용하고 있습니다.
A2: ③ 정답은 없다
- OOP, 함수형 프로그래밍, 디자인 패턴 등은 모두 도구일 뿐입니다.
- 중요한 것은 문제를 효과적으로 해결하는 것이며, 이를 위해 다양한 도구와 기법을 학습하고 적용하는 능력이 필요합니다.
- JavaScript와 React에서는 함수형 프로그래밍이 더 강조되는 경향이 있지만, 필요에 따라 OOP 기법을 적용할 수도 있습니다.
3.
Q3: react는 stateful 한가? 그렇다면 OOP가 맞을 수 있다는 뜻인가?
A3: ① React에서의 상태 관리와 프로그래밍 패러다임
먼저, React는 사용자 인터페이스를 구축하기 위한 JavaScript 라이브러리로, 주로 컴포넌트 기반으로 UI를 구성합니다. React에서는 상태(state)와 속성(props)를 사용하여 컴포넌트의 동작과 화면 표시를 제어합니다.
- 함수형 컴포넌트: React에서 함수형 컴포넌트는 함수로 정의되며, useState, useEffect와 같은 Hooks를 통해 상태를 관리합니다.
- 상태 관리: 상태를 관리한다는 점에서 React 애플리케이션은 stateful하다고 할 수 있습니다.
그럼 OOP가 더 적합한가요?
여기서 중요한 점은 상태(state)를 관리한다고 해서 반드시 객체지향 프로그래밍(OOP)이 더 적합하다는 의미는 아니라는 것입니다.
이유를 자세히 설명해 드리겠습니다:
- 상태 관리와 프로그래밍 패러다임은 별개의 개념입니다:
- 상태(state): 프로그램이 현재 가지고 있는 데이터나 상황을 의미합니다.
- 프로그래밍 패러다임: 상태를 어떻게 관리하고, 코드 구조를 어떻게 조직할 것인지에 대한 철학이나 접근 방식입니다.
- React는 함수형 프로그래밍의 철학을 적극적으로 활용합니다:
- 불변성(immutability): React에서는 상태를 직접 변경하지 않고, 새로운 상태 객체를 생성하여 상태를 업데이트합니다.
- 순수 함수(pure function): 입력값이 동일하면 항상 동일한 결과를 반환하고, 부수 효과(side effect)가 없는 함수를 지향합니다.
- Hooks의 사용: useState, useEffect 등은 함수형 컴포넌트 내에서 상태와 부수 효과를 관리하기 위한 도구로, 함수형 프로그래밍의 개념을 반영합니다.
- 상태 관리 방식의 차이:
- OOP에서는 객체가 상태와 해당 상태를 조작하는 메서드를 함께 캡슐화합니다.
- 함수형 프로그래밍에서는 상태를 불변으로 취급하며, 상태의 변경은 기존 상태를 변경하는 것이 아니라 새로운 상태를 생성하는 방식으로 처리합니다.
- React의 방향성:
- React는 초기에는 클래스 컴포넌트를 통해 OOP 스타일의 코딩을 지원했습니다.
- 하지만 Hooks의 도입 이후 함수형 컴포넌트가 주로 사용되며, 이는 React가 함수형 프로그래밍 패러다임을 더욱 강조하고 있다는 것을 의미합니다.
결론
- React 애플리케이션은 상태를 관리하기 때문에 stateful입니다.
- 그러나 상태를 관리한다는 이유만으로 OOP가 더 적합하다고 볼 수는 없습니다.
- React는 함수형 프로그래밍의 원칙과 패턴을 적극적으로 활용하고 있으며, 이는 상태 관리 방식에도 반영되어 있습니다.
- 따라서 React에서는 함수형 프로그래밍 패러다임이 더 적합하다고 볼 수 있습니다.
추가적인 고려 사항
- OOP의 장점은 상태와 행위를 하나의 객체로 묶어 복잡한 상태 변화를 관리하고, 상속과 다형성을 통해 코드 재사용성을 높이는 데 있습니다.
- 반면에 함수형 프로그래밍은 부수 효과를 최소화하고, 데이터의 불변성을 유지하며, 코드를 예측 가능하고 테스트하기 쉽게 만드는 데 초점을 둡니다.
- React의 컴포넌트들은 주로 순수 함수처럼 동작하며, 입력(props)이 동일하면 항상 동일한 결과를 반환하려고 합니다.
요약
- React에서의 상태 관리는 함수형 프로그래밍의 원칙에 따라 이루어집니다.
- 상태를 관리하지만, 이는 OOP가 아닌 함수형 프로그래밍의 방식으로 처리됩니다.
- 따라서, React 개발에서는 함수형 프로그래밍 패러다임이 더 적합하며, 이 패러다임을 이해하고 활용하는 것이 중요합니다.
4.
Q4: "혼합형 접근" 에 관한 추가 질문
"혼합형 접근" 에 대해 더 알고 싶은 것이 있습니다.
저는 react(next.js)에서 Three.js 인스턴스를 다뤄야 할 일이 있었습니다.
r3f 라는 라이브러리도 있었지만, r3f는 canvas 를 하나만 만들수 있는 줄 알았던 당시의 일입니다.
저는 웹 페이지에 3d를 다루는 canvas 를 두개 이상 넣어야 했습니다.
그래서 그냥 Three.js 의 방법으로 class 를 만들었습니다. class 는 그냥 일반적이었는데요.
scene, camera, renderer 등을 생성하고, gltf loader 로 파일을 로딩해서 보여주는 로직을 담고 있었습니다.
그런데 몇가지 문제가 생겼습니다.
인스턴스를 생성하고 메서드들을 사용해야 하는데 우선 생성부터 어디서 해야할지 막막했고,
결국 useEffect를 빈 의존성 배열로 하여 거기서 ref.current 에 인스턴스를 담았습니다.
그리고 필요한 곳에서 ref.current.메서드명() 같은 식으로 썼는데,
render 와 requestAnimationFrame 에서부터 의도하지 않은 동작들이 발생했습니다.
바닐라 자바스크립트에서는 문제가 없던 것들인데 해결하지 못했고,
결국 인스턴스 메서드로 있던 render를 리액트 컴포넌트의 useEffect에서 다음과 같이 실행하였습니다.
const animate = () => {
appRef.current?.render();
rafRef.current = requestAnimationFrame(animate);
};
animate();
이 외에도 문제가 많았는데, 인스턴스가 생성될 때 파라미터로 어떤 상태를 받고
그것을 this.상태 = 파라미터 같은식으로 사용하자니
class 외부의 상태가 변할때마다 처리하기가 번거롭고
Three 인스턴스가 여러개 생겨버린다거나, 여하튼 문제가 많았습니다.
이런 사례가 혹시 react의 함수형프로그래밍 패러다임과 OOP가 충돌하는 사례일 수 있을까요?
A4: 문제의 원인: React의 함수형 패러다임과 OOP의 충돌
React는 함수형 프로그래밍 패러다임을 적극적으로 활용하는 라이브러리입니다.
반면에 Three.js는 객체지향 프로그래밍(OOP) 스타일로 설계된 라이브러리입니다.
이 두 가지 패러다임을 함께 사용할 때,
특히 React의 함수형 컴포넌트 내에서 OOP 스타일의 클래스를 사용하려고 하면 여러 문제가 발생할 수 있습니다.
당신이 겪은 문제들은 다음과 같은 이유로 발생했을 가능성이 높습니다:
- 생명주기 관리의 복잡성: React의 컴포넌트 생명주기와 클래스 인스턴스의 생명주기를 동기화하기가 어렵습니다. 특히 여러 번의 재렌더링이 발생하면 클래스 인스턴스가 의도하지 않게 여러 개 생성될 수 있습니다.
- 상태 관리의 불일치: React는 상태와 UI를 동기화하는 데 특화되어 있지만, 클래스 내부에서 관리되는 상태는 React의 상태 관리 시스템과 별개로 동작합니다. 이는 예상치 못한 동작이나 버그를 유발할 수 있습니다.
- 렌더링 및 업데이트 문제: 클래스 인스턴스 내부에서 requestAnimationFrame을 사용하여 렌더링 루프를 관리할 때, React의 렌더링 흐름과 충돌이 발생할 수 있습니다.
React의 함수형 패러다임과 OOP의 통합 문제
React는 상태 변경 시 컴포넌트를 다시 렌더링하여 UI를 업데이트합니다. 그러나 OOP 스타일로 작성된 클래스 인스턴스는 내부 상태 변화에 따라 자체적으로 UI를 업데이트하려고 합니다. 이로 인해 React의 렌더링 메커니즘과 클래스 인스턴스의 상태 관리가 서로 충돌하게 됩니다.
해결 방안
이러한 문제를 해결하기 위해 몇 가지 접근 방법을 고려해 볼 수 있습니다.
1. react-three-fiber(R3F) 사용 검토
react-three-fiber는 Three.js를 React의 선언적 렌더링과 통합할 수 있도록 도와주는 라이브러리입니다. R3F를 사용하면 React의 생명주기와 상태 관리 시스템을 활용하면서 Three.js로 3D 장면을 구성할 수 있습니다.
- 여러 개의 Canvas 사용: R3F에서 여러 개의
<Canvas>
컴포넌트를 사용할 수 있습니다.
import { Canvas } from '@react-three/fiber';
function App() {
return (
<>
<Canvas>
{/* 첫 번째 3D 장면 */}
</Canvas>
<Canvas>
{/* 두 번째 3D 장면 */}
</Canvas>
</>
);
}
- 장점: React의 상태 관리와 Hooks를 활용하여 3D 장면을 구성할 수 있으며, 생명주기 관리도 React와 일치합니다.
2. 클래스 대신 함수와 Hooks 사용
클래스를 사용하지 않고 함수형 컴포넌트와 Hooks를 사용하여 Three.js 인스턴스를 관리하면 React와의 통합이 더 원활해집니다.
- 예시 코드:
import React, { useRef, useEffect } from 'react';
import * as THREE from 'three';
function ThreeScene() {
const mountRef = useRef(null);
useEffect(() => {
// Three.js 초기화
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
mountRef.current.clientWidth / mountRef.current.clientHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(
mountRef.current.clientWidth,
mountRef.current.clientHeight
);
mountRef.current.appendChild(renderer.domElement);
// 애니메이션 루프
const animate = function () {
requestAnimationFrame(animate);
// 객체 회전 등의 업데이트
renderer.render(scene, camera);
};
animate();
// 컴포넌트 언마운트 시 정리
return () => {
renderer.dispose();
mountRef.current.removeChild(renderer.domElement);
};
}, []);
return <div ref={mountRef} style={{ width: '100%', height: '100%' }} />;
}
- 장점: React의 생명주기와 일치하므로 메모리 누수나 의도하지 않은 인스턴스 생성 등의 문제가 줄어듭니다.
3. 상태를 React로 끌어오기
Three.js에서 사용하는 상태(예: 객체의 위치, 회전 등)를 React의 상태로 관리하고, 이를 useEffect를 통해 Three.js 장면에 반영합니다.
- 예시 코드:
import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
function ThreeScene() {
const mountRef = useRef(null);
const [rotation, setRotation] = useState(0);
useEffect(() => {
// Three.js 초기화
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
mountRef.current.clientWidth / mountRef.current.clientHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(
mountRef.current.clientWidth,
mountRef.current.clientHeight
);
mountRef.current.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
// 애니메이션 루프
const animate = function () {
requestAnimationFrame(animate);
cube.rotation.x += rotation;
cube.rotation.y += rotation;
renderer.render(scene, camera);
};
animate();
// 컴포넌트 언마운트 시 정리
return () => {
renderer.dispose();
mountRef.current.removeChild(renderer.domElement);
};
}, [rotation]);
// rotation 상태 변경 예시
useEffect(() => {
const interval = setInterval(() => {
setRotation((prev) => prev + 0.01);
}, 16);
return () => clearInterval(interval);
}, []);
return <div ref={mountRef} style={{ width: '100%', height: '100%' }} />;
}
- 장점: React의 상태 변화를 Three.js 장면에 반영할 수 있어 상태 관리가 일관됩니다.
4. 클래스 인스턴스 관리 개선
클래스를 반드시 사용해야 하는 경우, 클래스 인스턴스의 생성과 생명주기를 React의 생명주기와 정확히 동기화해야 합니다.
useRef와 useEffect의 적절한 사용: 클래스 인스턴스를 useRef로 저장하고, useEffect의 의존성 배열을 신중하게 관리하여 인스턴스가 불필요하게 재생성되지 않도록 합니다.
- 예시 코드:
import React, { useRef, useEffect } from 'react';
class ThreeApp {
constructor(container) {
// 초기화 코드
}
render() {
// 렌더링 코드
}
dispose() {
// 정리 코드
}
}
function ThreeComponent() {
const appRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
appRef.current = new ThreeApp(containerRef.current);
appRef.current.render();
return () => {
appRef.current.dispose();
};
}, []);
return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
}
- 주의사항: React의 상태나 props가 변경될 때 클래스 인스턴스가 재생성되지 않도록 useEffect의 의존성 배열을 빈 배열로 유지하거나, 필요한 경우에만 업데이트되도록 합니다.
결론
당신이 겪은 문제는 React의 함수형 프로그래밍 패러다임과 Three.js의 OOP 스타일이 충돌하면서 발생한 것으로 보입니다. 특히 클래스 인스턴스의 관리와 React의 생명주기 관리 방식이 잘 맞지 않아 문제가 발생했습니다.
이를 해결하기 위해서는 다음과 같은 방법을 고려해 볼 수 있습니다:
- react-three-fiber 사용: 이 라이브러리를 통해 React와 Three.js를 자연스럽게 통합할 수 있습니다.
- 함수와 Hooks 사용: 클래스 대신 함수형 컴포넌트와 Hooks를 사용하여 Three.js 인스턴스를 관리하면 React의 상태 관리와 생명주기와 일치시킬 수 있습니다.
- 상태 관리 일원화: Three.js의 상태를 React의 상태로 관리하여 상태 변화에 따른 UI 업데이트를 React 방식으로 처리합니다.
요약
- React의 함수형 패러다임과 Three.js의 OOP 스타일은 충돌할 수 있습니다.
- 클래스 인스턴스의 생명주기 관리와 상태 관리가 주요 문제의 원인입니다.
- 함수형 컴포넌트와 Hooks를 사용하여 Three.js를 관리하거나, react-three-fiber와 같은 라이브러리를 사용하면 문제를 해결할 수 있습니다.
'TIL' 카테고리의 다른 글
[241017 TIL] CORS 정리 (0) | 2024.10.17 |
---|---|
[241017 TIL] 디바운싱,쓰로틀링 Closure + HOC로 만들어보기 (0) | 2024.10.17 |
[241011 TIL] 싱글톤(Singleton) 패턴(gpt 질의) (1) | 2024.10.10 |
[241010 TIL] Parallel, Intercepting routes 사용법(모달) (0) | 2024.10.10 |
[241007 TIL] git credential 이상할때 (1) | 2024.10.06 |