모듈 패턴 (Module Pattern)
• 용도: 변수와 함수를 캡슐화하여 외부에 노출되는 부분을 제어합니다.
• 장점: 전역 변수 오염을 줄이고, 코드의 가독성 및 재사용성을 높입니다.
• 예시: ES6 모듈(import / export)과 IIFE(즉시 실행 함수 표현식)으로 구현할 수 있습니다.
const CounterModule = (() => {
let count = 0;
return {
increment: () => ++count,
getCount: () => count,
};
})();
옵저버 패턴 (Observer Pattern)
• 용도: 객체 간의 이벤트 기반 상호작용을 위한 패턴으로, 한 객체의 상태가 변경되면 이를 자동으로 구독자에게 알립니다.
• 장점: 리액티브한 UI 개발에 유용하며, 컴포넌트 간 데이터 흐름을 관리하기 좋습니다.
• 예시: 리액트의 useEffect 훅이나 RxJS에서 자주 사용됩니다.
// 간단한 옵저버 패턴 예시
class Observable {
constructor() {
this.subscribers = [];
}
subscribe(fn) {
this.subscribers.push(fn);
}
notify(data) {
this.subscribers.forEach(fn => fn(data));
}
}
싱글톤 패턴 (Singleton Pattern)
• 용도: 클래스가 단 하나의 인스턴스만 존재하도록 보장하는 패턴입니다.
• 장점: 애플리케이션에서 전역 상태 관리나 설정을 공유해야 할 때 유용합니다.
• 예시: React의 전역 상태 관리 도구들, 예를 들어 Redux 스토어 등이 싱글톤 패턴으로 사용됩니다.
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this;
}
return Singleton.instance;
}
}
프록시 패턴 (Proxy Pattern)
• 용도: 실제 객체에 접근하기 전에 중간에서 통제 역할을 수행합니다. 캐싱, 로깅, 인증 등에 유용합니다.
• 장점: 원래 객체를 안전하게 보호하며, 여러 부가 기능을 쉽게 추가할 수 있습니다.
• 예시: Proxy 객체를 사용해 API 호출의 캐싱 등을 관리할 수 있습니다.
const apiProxy = new Proxy(api, {
get(target, prop) {
console.log(`Getting ${prop}`);
return target[prop];
}
});
팩토리 패턴 (Factory Pattern)
• 용도: 객체 생성을 담당하는 패턴으로, 객체 생성 로직을 캡슐화하여 코드의 유연성을 높입니다.
• 장점: 다양한 객체를 생성해야 할 때 코드 중복을 줄이고 유지보수를 쉽게 합니다.
• 예시: 조건에 따라 다양한 컴포넌트를 생성하는 함수.
function createButton(type) {
if (type === 'primary') {
return <PrimaryButton />;
} else if (type === 'secondary') {
return <SecondaryButton />;
}
}
컨테이너-프레젠테이션 패턴 (Container-Presenter Pattern)
• 용도: 리액트나 뷰 같은 프레임워크에서 컴포넌트의 상태 관리와 UI 렌더링을 분리합니다.
• 장점: UI와 비즈니스 로직이 분리되어 컴포넌트 재사용성이 높아집니다.
• 예시: Container 컴포넌트는 상태와 로직을 관리하고, Presenter 컴포넌트는 UI만 렌더링합니다.
const UserContainer = () => {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
return <UserPresenter user={user} />;
};
상태 패턴 (State Pattern)
• 용도: 객체가 상태에 따라 다양한 동작을 가지도록 합니다. 상태를 객체로 만들어 관리하므로 상태 전환이 명확해집니다.
• 장점: 상태에 따라 변화하는 로직을 명확하게 정의할 수 있어 가독성과 유지보수성을 높입니다.
• 예시: 컴포넌트가 여러 상태를 가지는 상황에서, 상태를 객체로 만들어 관리합니다.
const stateHandlers = {
loading: () => <Loading />,
success: () => <Success />,
error: () => <Error />
};
const MyComponent = ({ state }) => stateHandlers[state]();
프론트엔드에서 이 패턴들을 잘 이해하고 적용하면, 유지보수성과 확장성이 높은 코드를 작성하는 데 큰 도움이 됩니다.