Frontend 구조 설계서 & 상세 설계

SIMWOOHYUN·2025년 5월 30일
# Frontend 구조 설계서 & 상세 설계

## 1. 개요
- 목적: React 기반 SPA(단일 페이지 애플리케이션)의 확장성과 재사용성을 고려한 FE 구조 설계  
- 대상: UI 컴포넌트 단위의 상세 설계 및 구현 가이드  

---

## 2. FE 아키텍처 구조 설계

### 2.1 폴더 구조

.
├─ public/
│ └─ index.html
├─ src/
│ ├─ assets/ # 이미지, 폰트, 스타일 등 정적 자산
│ ├─ components/ # 재사용 가능한 UI 컴포넌트
│ │ ├─ Button/
│ │ │ ├─ Button.tsx
│ │ │ ├─ Button.styles.ts
│ │ │ └─ index.ts
│ │ ├─ Modal/
│ │ │ ├─ Modal.tsx
│ │ │ ├─ Modal.styles.ts
│ │ │ └─ index.ts
│ │ └─ Card/
│ │ ├─ Card.tsx
│ │ ├─ Card.styles.ts
│ │ └─ index.ts
│ ├─ pages/ # 라우트 단위 페이지 컴포넌트
│ ├─ hooks/ # 커스텀 훅
│ ├─ contexts/ # React Context
│ ├─ services/ # API 호출 모듈
│ ├─ utils/ # 범용 유틸 함수
│ ├─ App.tsx
│ └─ index.tsx
├─ package.json
└─ tsconfig.json


### 2.2 컴포넌트 분류
1. **Atoms**: 버튼, 입력창 등 가장 작은 단위  
2. **Molecules**: 아톰을 조합한 간단한 UI 블록 (e.g. 검색 바)  
3. **Organisms**: 모듈 단위의 큰 구조 (e.g. 헤더, 푸터)  
4. **Templates / Pages**: 실제 화면 

---

## 3. 상세 설계: UI 컴포넌트

### 3.1 Button 컴포넌트

#### 3.1.1 설계 개요
- **역할**: 클릭 가능한 버튼  
- **사용 시나리오**: 폼 제출, 페이지 이동, 액션 트리거  

#### 3.1.2 Props 정의
```ts
// src/components/Button/Button.tsx
export interface ButtonProps {
  children: React.ReactNode;
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  onClick?: () => void;
}

3.1.3 스타일 설계

// src/components/Button/Button.styles.ts
import { css } from '@emotion/react';

export const baseStyle = css`
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 500;
`;

export const variantStyles = {
  primary: css`
    background-color: #0070f3;
    color: #fff;
  `,
  secondary: css`
    background-color: #eaeaea;
    color: #000;
  `,
  danger: css`
    background-color: #e00;
    color: #fff;
  `,
};

export const sizeStyles = {
  small: css`padding: 4px 8px; font-size: 0.8rem;`,
  medium: css`padding: 8px 16px; font-size: 1rem;`,
  large: css`padding: 12px 24px; font-size: 1.2rem;`,
};

3.1.4 구현

// src/components/Button/Button.tsx
import React from 'react';
import { ButtonProps } from './Button.props';
import { baseStyle, variantStyles, sizeStyles } from './Button.styles';

const Button: React.FC<ButtonProps> = ({
  children,
  variant = 'primary',
  size = 'medium',
  disabled = false,
  onClick,
}) => (
  <button
    css={[baseStyle, variantStyles[variant], sizeStyles[size]]}
    disabled={disabled}
    onClick={onClick}
  >
    {children}
  </button>
);

export default Button;

3.2 Modal 컴포넌트

3.2.1 설계 개요

  • 역할: 오버레이 방식 모달 창
  • 사용 시나리오: 경고, 폼, 상세 정보 표시

3.2.2 Props 정의

// src/components/Modal/Modal.tsx
export interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  title?: string;
  children: React.ReactNode;
}

3.2.3 스타일 설계

// src/components/Modal/Modal.styles.ts
import { css } from '@emotion/react';

export const overlayStyle = css`
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex; align-items: center; justify-content: center;
`;

export const contentStyle = css`
  background: #fff;
  border-radius: 8px;
  max-width: 500px;
  width: 100%;
  padding: 16px;
`;

3.2.4 구현

// src/components/Modal/Modal.tsx
import React from 'react';
import { ModalProps } from './Modal.props';
import { overlayStyle, contentStyle } from './Modal.styles';
import Button from '../Button';

const Modal: React.FC<ModalProps> = ({ isOpen, onClose, title, children }) => {
  if (!isOpen) return null;
  return (
    <div css={overlayStyle}>
      <div css={contentStyle}>
        {title && <h2>{title}</h2>}
        <div>{children}</div>
        <Button variant="secondary" size="small" onClick={onClose}>
          닫기
        </Button>
      </div>
    </div>
  );
};

export default Modal;

3.3 Card 컴포넌트

3.3.1 Props 정의

// src/components/Card/Card.props.ts
export interface CardProps {
  header?: React.ReactNode;
  footer?: React.ReactNode;
  children: React.ReactNode;
}

3.3.2 구현

// src/components/Card/Card.tsx
import React from 'react';
import { CardProps } from './Card.props';
import { css } from '@emotion/react';

const containerStyle = css`
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;

const headerStyle = css`margin-bottom: 12px; font-weight: 600;`;
const footerStyle = css`margin-top: 12px; font-size: 0.9rem; color: #666;`;

const Card: React.FC<CardProps> = ({ header, footer, children }) => (
  <div css={containerStyle}>
    {header && <div css={headerStyle}>{header}</div>}
    <div>{children}</div>
    {footer && <div css={footerStyle}>{footer}</div>}
  </div>
);

export default Card;

4. 결론 및 활용 방안

  • 확장성: 새로운 Variant, Size를 추가해도 기존 코드 건드림 없이 확장 가능
  • 재사용성: 공통 스타일·로직은 hooks/services로 분리 권장
  • 다음 단계:
    1. Theme Context 연동
    2. Storybook 도입하여 컴포넌트 문서화
    3. 테스트(Jest, React Testing Library) 작성

위 설계를 기반으로 실제 프로젝트에 적용하면, 모듈화된 FE 구조와 일관된 UI 컴포넌트를 손쉽게 관리·확장할 수 있습니다.

0개의 댓글