본문 바로가기

[IT/Programming]/HTML related

프론트엔드 관점에서의 SOLID 원칙

반응형
m.logPrint() is working!

<eq> and <eqq> tags are rendered to MathJax format, being enclosed by \ ( \ ) and \ [ \ ].

docuK1 scripts started!
If this log is not closed automatically, there must be an error somewhere in your document or scripts.

Table of Contents is filled out.

Auto numberings of sections (div.sec>h2, div.subsec>h3, div.subsubsec>h4), <eqq> tags, and <figure> tags are done.

<cite> and <refer> tags are rendered to show bubble reference.

<codeprint> tags are printed to corresponding <pre> tags, only when the tags exist in the document.


Current styles (dark/bright mode, font-family, font-size, line-height) are shown.

disqus.js with id="disqus-js" is loaded.

kakao.js with id="kakao-js-sdk" is loaded.

New ShortKeys (T: Table of Contents, F: Forward Section, D: Previous Section, L: To 전체목록/[Lists]) are set.

m.delayPad=0;
m.wait=1024;
wait 794ms.
Doing delayed-load. : 1
▼ Hide
Toggle a mess
Go (FS)
TofC
DocuK Log
Backward
Forward
RRA
Lists
CmtZ
CmtX
Handle CmtZ
Log in
out focus
이 글이 도움이 되셨다면, 광고 클릭 한번씩만 부탁드립니다 =ㅂ=ㅋ. (If this article was helpful, please click the ad once. Thank you. ;)
Mode: Bright; Font: Noto Sans KR; font-size: 18.0px (10.0); line-height: 1.6;
width: 1280, height: 720, version: 3.1.1
Canonical URI: https://kipid.tistory.com/entry/프론트엔드-관점에서의-SOLID-원칙
dg:plink (Document Global Permanent Link): https://kipid.tistory.com/396
document.referrer: Empty
This document is rendered by docuK (See also SEE (Super Easy Edit) of docuK and pure SEE).

프론트엔드 관점에서의 SOLID 원칙

SOLID 원칙은 객체 지향 설계(Object-Oriented Design, OOD)에서 코드의 유지보수성과 확장성을 높이기 위해 제안된 5가지 설계 원칙입니다. 원래는 백엔드 개발과 소프트웨어 공학에서 주로 논의되었지만, 프론트엔드 개발에서도 특히 리액트(React)와 같은 컴포넌트 기반 프레임워크를 사용할 때 적용할 수 있습니다. 프론트엔드 관점에서 SOLID 원칙을 설명하고, 리액트와 같은 환경에서 어떻게 활용되는지 예시와 함께 풀어보겠습니다.

TPH1.Posting History

▼ Show/Hide

T1.단일 책임 원칙 (Single Responsibility Principle, SRP)

▼ Show/Hide
정의: 한 클래스(또는 컴포넌트)는 단 하나의 책임만 가져야 하며, 그 책임이 변경될 이유도 단 하나여야 한다.
프론트엔드에서의 의미: 컴포넌트가 UI 렌더링, 데이터 fetching, 상태 관리 등 여러 역할을 동시에 수행하지 않도록 분리해야 한다.
예시:
// SRP 위반: UI 렌더링과 데이터 fetching을 동시에 수행
function UserProfile() {
    const [user, setUser] = useState(null);

    useEffect(() => {
        fetch('/api/user')
            .then(res => res.json())
            .then(data => setUser(data));
    }, []);

    return <div>{user ? user.name : 'Loading...'}</div>;
}

// SRP 준수: 데이터 fetching과 UI 렌더링 분리
function useUserData() {
    const [user, setUser] = useState(null);
    useEffect(() => {
        fetch('/api/user')
            .then(res => res.json())
            .then(data => setUser(data));
    }, []);
    return user;
}

function UserProfile() {
    const user = useUserData();
    return <div>{user ? user.name : 'Loading...'}</div>;
}
장점: 컴포넌트가 단일 책임을 가지면 재사용성이 높아지고, 테스트와 디버깅이 쉬워진다.
▲ Hide

T2.개방-폐쇄 원칙 (Open/Closed Principle, OCP)

▼ Show/Hide
정의: 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 열려 있어야 하고, 수정에는 닫혀 있어야 한다.
프론트엔드에서의 의미: 기존 컴포넌트를 수정하지 않고도 새로운 기능을 추가할 수 있어야 한다. props나 컴포지션을 활용해 확장성을 확보한다.
예시:
// OCP 위반: 버튼 스타일을 추가하려면 컴포넌트 수정 필요
function Button() {
    return <button style={{ background: 'blue', color: 'white' }}>Click</button>;
}

// OCP 준수: props로 스타일 확장 가능
function Button({ style, children }) {
    return <button style={{ background: 'blue', color: 'white', ...style }}>{children}</button>;
}

// 사용
<Button style={{ borderRadius: '5px' }}>Click Me</Button>
장점: 새로운 요구사항(예: 다른 스타일의 버튼)이 생겨도 기존 코드를 수정하지 않고 확장 가능.
▲ Hide

T3.리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

▼ Show/Hide
정의: 자식 클래스는 부모 클래스를 대체할 수 있어야 하며, 프로그램의 동작이 변하지 않아야 한다.
프론트엔드에서의 의미: 컴포넌트의 계층 구조나 인터페이스를 사용할 때, 하위 컴포넌트가 상위 컴포넌트의 역할을 문제없이 수행할 수 있어야 한다.
예시:
// LSP 위반: 하위 컴포넌트가 상위의 기대를 어김
function BaseComponent({ data }) {
    return <div>{data.title}</div>;
}

function SpecialComponent({ data }) {
    if (!data) return null; // data가 없으면 렌더링 안 함 (기대와 다름)
}

// LSP 준수: 하위 컴포넌트가 상위와 동일한 동작 보장
function SpecialComponent({ data }) {
    return <div>{data.title} (Special)</div>;
}

function App() {
    const component = Math.random() > 0.5 ? BaseComponent : SpecialComponent;
    return React.createElement(component, { data: { title: 'Hello' } });
}
장점: 컴포넌트를 교체해도 일관된 동작을 보장해 코드 안정성이 높아진다.
▲ Hide

T4.인터페이스 분리 원칙 (Interface Segregation Principle, ISP)

▼ Show/Hide
정의: 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다.
프론트엔드에서의 의미: 컴포넌트가 필요 없는 props나 기능을 강제로 받지 않도록 설계한다. 필요한 기능만 제공하는 작은 단위로 나눈다.
예시:
// ISP 위반: 불필요한 props까지 강제 전달
function BigComponent({ name, age, email, onClick }) {
    return <button onClick={onClick}>{name}</button>;
}

// ISP 준수: 필요한 props만 사용
function SimpleButton({ label, onClick }) {
    return <button onClick={onClick}>{label}</button>;
}

// 사용
<SimpleButton label="Click" onClick={() => console.log('clicked')} />
장점: 컴포넌트가 불필요한 의존성을 갖지 않아 간결하고 유연해진다.
▲ Hide

T5.의존성 역전 원칙 (Dependency Inversion Principle, DIP)

▼ Show/Hide
정의: 고수준 모듈은 저수준 모듈에 의존하지 않아야 하며, 둘 다 추상화에 의존해야 한다.
프론트엔드에서의 의미: 컴포넌트가 구체적인 구현(예: 특정 API 호출)에 직접 의존하지 않고, 추상화된 인터페이스나 함수에 의존하도록 설계한다.
예시:
// DIP 위반: 컴포넌트가 구체적인 fetch에 의존
function UserList() {
    const [users, setUsers] = useState([]);
    useEffect(() => {
        fetch('/api/users').then(res => res.json()).then(setUsers);
    }, []);
    return <ul>{users.map(user => <li>{user.name}</li>)}</ul>;
}

// DIP 준수: 데이터 fetching을 추상화에 의존
function useData(fetchFn) {
    const [data, setData] = useState([]);
    useEffect(() => {
        fetchFn().then(setData);
    }, [fetchFn]);
    return data;
}

function UserList({ fetchUsers }) {
    const users = useData(fetchUsers);
    return <ul>{users.map(user => <li>{user.name}</li>)}</ul>;
}

// 사용
const fetchUsers = () => fetch('/api/users').then(res => res.json());
<UserList fetchUsers={fetchUsers} />
장점: 데이터 소스를 변경(예: API에서 로컬 데이터로)해도 컴포넌트를 수정할 필요가 없어 유연성이 높아진다.
▲ Hide

T6.프론트엔드에서 SOLID 적용의 현실적 고려사항

▼ Show/Hide
컴포넌트 분리: SRP와 ISP를 준수하려면 컴포넌트를 작고 단일 목적으로 유지하는 것이 중요. 예를 들어, <Form><Input>을 분리.
훅 활용: useEffect, useState 같은 훅을 통해 로직을 분리하면 DIP와 SRP를 쉽게 적용 가능.
타입스크립트와 결합: TypeScript의 인터페이스를 사용하면 OCP와 LSP를 더 명확히 구현할 수 있음.
현실적 한계: 프론트엔드는 UI와 밀접하게 연관되어 있어 완벽한 객체 지향 설계보다 실용성이 우선될 때도 있음. 과도한 추상화는 코드 복잡성을 높일 수 있으니 균형이 필요.
▲ Hide

T7.결론

▼ Show/Hide
프론트엔드에서 SOLID 원칙을 적용하면 재사용성, 유지보수성, 확장성이 높은 코드를 작성할 수 있습니다. 리액트에서는 컴포넌트와 훅을 활용해 이 원칙을 자연스럽게 구현할 수 있으며, 특히 대규모 프로젝트에서 코드 구조를 체계적으로 관리하는 데 큰 도움이 됩니다. 다만, 프론트엔드의 특성상 UI와의 결합도를 완전히 없애기는 어렵기 때문에, 원칙을 맹목적으로 따르기보다는 프로젝트의 요구사항에 맞춰 적절히 조정하는 것이 중요합니다!
▲ Hide
이 글이 도움이 되셨다면, 광고 클릭 한번씩만 부탁드립니다 =ㅂ=ㅋ. (If this article was helpful, please click the ad once. Thank you. ;)
반응형
Get page views