ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Cursor에서 Figma to React rule 적용
    Language/React 2025. 3. 10. 18:14

    Figma 디자인을 React 컴포넌트로 수작업으로 변환하는 과정은 시간이 많이 소요되고 반복적인 작업이다.
    이 과정은 효율적이지 못하며, 자동화를 통해 개선할 필요가 있다고 생각되었다.
    초기 목표는 Figma API를 활용하여 디자인 토큰을 추출하고, 이를 바탕으로 React 코드를 자동 생성하는 것이었다. 그러나 여러 기술적 한계로 인해 완벽한 자동화에 실패하였다.
    특히, Figma의 디자인을 AI를 사용하지 않고 완벽하게 React 코드로 재현하는 것은 몇일간 테스트한 결과 한계가 있는듯 보인다. (더이상 시간투자 X)

    그래도 노가다 작업을 줄이기 위해서 작업 방향을 급선회 하여, Cursor에서 제공하는 규칙을 사용하여 Figma 디자인에서 React 컴포넌트로의 변환 시간을 단축시키는 방식으로 정리해 둔다.

     

    1. Cursor rule 작성

    1.1. rule 생성

    • figma-to-react-rule 추가

    figma-to-react-rule

    • figma-to-react-rule.mdc
    # Figma to React 변환 규칙
    
    ## ComponentName 규칙
    1. CSS 모듈 파일명은 컴포넌트 이름을 카멜케이스로 작성
    2. 예시: `Button.tsx` 컴포넌트의 경우 `Button`
    3. 예시: `UserProfile.tsx` 컴포넌트의 경우 `UserProfile`
    4. 예시: `UCareMainPage.tsx` 컴포넌트의 경우 `UCareMainPage`
    
    ## 기본 구조
    ```typescript
    import React from 'react';
    
    import classNames from 'classnames/bind';
    import styles from 'css/{componentNameCamelCase}.module.css';
    
    const cx = classNames.bind(styles);
    
    interface I{ComponentName}Props {
      // props 정의
    }
    
    const {ComponentName}: React.FC<I{ComponentName}Props> = ({ /* props */ }) => {
      return (
        // JSX
      );
    };
    
    export default {ComponentName};
    ```
    
    ## CSS 모듈 파일명 규칙
    1. CSS 모듈 파일명은 컴포넌트 이름을 카멜케이스로 작성
    2. 예시: `Button.tsx` 컴포넌트의 경우 `button.module.css`
    3. 예시: `UserProfile.tsx` 컴포넌트의 경우 `userProfile.module.css`
    4. 예시: `UCareMainPage.tsx` 컴포넌트의 경우 `uCareMainPage.module.css`
    5. CSS 모듈 파일이 존재하지 않는 경우 새로 생성할 것
    
    ## CSS 모듈 파일 생성 규칙
    1. 컴포넌트에 해당하는 CSS 모듈 파일이 없는 경우 새로 생성
    2. 파일 경로: `css/{componentNameCamelCase}.module.css`
    3. 기본 구조:
    ```css
    /* 빈 CSS 파일 */
    ```
    
    
    # Figma to React 변환 규칙
    
    ## 이미지 리소스 규칙
    
    1. 이미지 import 규칙
       ```typescript
       // 모든 이미지는 반드시 'images/copy.svg' 경로로 import
       import calendarSearchIcon from 'images/copy.svg';
       import graphIcon from 'images/copy.svg';
       ```
    
    2. 이미지 태그 작성 규칙
       ```typescript
       // 올바른 예시
       <img className={cx('iconCalendarSearch')} alt="달력 검색 아이콘" src={calendarSearchIcon} />
       <img className={cx('iconGraph')} alt="그래프 아이콘" src={graphIcon} />
       ```
    
    3. 네이밍 컨벤션
       - import 변수명: camelCase + Icon 접미사 (예: calendarSearchIcon)
       - className: 'icon' + PascalCase (예: iconCalendarSearch)
       - 모든 이미지 className은 'icon' 접두사로 시작
    
    4. 필수 규칙
       - 모든 이미지 경로는 'images/copy.svg'로 통일
       - alt 텍스트는 반드시 의미있는 설명 포함
       - className은 반드시 cx() 함수로 바인딩
    
    ## 네이밍 규칙
    1. 컴포넌트 이름은 PascalCase로 작성
    2. 인터페이스는 'I' 접두사 사용 (예: ICardProps)
    3. CSS 모듈 파일은 컴포넌트와 동일한 이름 사용
    
    ## 스타일 적용 규칙
    1. CSS Module 사용
    2. classNames/bind를 사용하여 스타일 바인딩
    3. className 적용 시 cx() 함수 사용
       ```typescript
       <div className={cx('className')}></div>
       ```
    
    ## 컴포넌트 Props 타입 정의
    ```typescript
    interface IComponentProps {
      // required props
      requiredProp: string;
      // optional props
      optionalProp?: number;
    }
    ```
    
    ## 예시 코드
    ```typescript
    import React from 'react';
    import classNames from 'classnames/bind';
    import styles from 'css/Card.module.css';
    
    const cx = classNames.bind(styles);
    
    interface ICardProps {
      roomNumber?: string;
      valueType?: string;
      capacity?: string;
      residentName?: string;
      endDate?: string;
      remainingDays?: number;
    }
    
    const Card: React.FC<ICardProps> = ({
      roomNumber,
      valueType,
      capacity,
      residentName,
      endDate,
      remainingDays,
    }) => {
      return (
        <div className={cx('card')}>
          <div className={cx('title')}>
            <div className={cx('title1')}>
              <b className={cx('b')}>{roomNumber}</b>
              <img className={cx('iconright')} alt="right arrow" src="Right.svg" />
            </div>
            <div className={cx('textGroup')}>
              <div className={cx('value')}>{valueType}</div>
              <div className={cx('divider')} />
              <div className={cx('value')}>{capacity}</div>
            </div>
          </div>
          <div className={cx('list')}>
            <div className={cx('group')}>
              <div className={cx('div')}>{residentName}</div>
              <div className={cx('textGroup1')}>
                <div className={cx('yymmdd')}>{endDate}</div>
                <div className={cx('nn')}>{remainingDays}일 남음</div>
              </div>
            </div>
          </div>
        </div>
      );
    };
    
    export default Card;
    ```
    
    ## 주의사항
    1. 모든 props는 타입 정의 필수
    2. 불필요한 div 중첩 최소화
    3. 재사용 가능한 컴포넌트 분리
    4. 성능 최적화 고려 (React.memo 등)
    5. 접근성 고려 (ARIA 속성 등)

     

    2. Figma에서 React 코드 가져오기

    2.1. Figma에서 가져올 컴포넌트 클릭 후 Inspect의 Code 영역에서 Locofy Lightning -> React로 코드를 가져온다.

     

    3. Cursor에서 Rule 적용하기

    3.1. Figma에서 복사한 code 넣기

     

    3.2. 미리 작성해둔 figma-to-react-rule.mdc를 적용

    UCareMainPage.tsx

     

    3.3. css 적용

    uCare.module.css

     

    3.4. css rule 적용

    # React 컴포넌트 기반 CSS 중첩 구조 변환 규칙
    
    ## 분석 단계
    1. JSX 구조 분석
       - 컴포넌트의 DOM 계층 구조를 파악합니다
       - className={cx('...')} 패턴을 식별합니다
       - 부모-자식 관계에 있는 요소들의 클래스명을 추적합니다
    
    2. CSS 클래스 매핑
       - 모든 cx('...') 호출을 찾아 클래스명 목록을 수집합니다
       - 각 클래스명이 사용된 JSX 요소의 위치와 중첩 깊이를 기록합니다
       - 동일한 클래스명이 여러 위치에서 사용된 경우 모든 인스턴스를 추적합니다
    
    ## 변환 단계
    1. 루트 요소 식별
       - 최상위 컨테이너 요소의 클래스를 루트 선택자로 사용합니다
       - 예: <div className={cx('allFrame')}>는 .allFrame이 루트 선택자가 됩니다
    
    2. 계층 구조 생성
       - JSX 구조를 따라 CSS 선택자를 중첩시킵니다
       - 직접적인 부모-자식 관계는 공백으로 연결합니다
       - 예: 
         * .parent { ... }
         * .parent .child { ... }
         * .parent .child .grandchild { ... }
    
    3. 스타일 속성 유지
       - 원래 CSS 모듈의 모든 스타일 속성을 그대로 유지합니다
       - 선택자만 중첩 구조로 변경합니다
    
    ## 특별 처리 규칙
    1. 동적 클래스명 처리
       - 조건부 클래스(cx('class1', condition && 'class2'))의 경우 기본 클래스를 중심으로 구조화합니다
       - 동적 클래스는 독립적인 중첩 규칙으로 처리합니다
    
    2. 중첩 깊이 최적화
       - 중첩이 4단계 이상 깊어지면 중간 단계를 생략할 수 있습니다
       - 불필요한 중첩은 피하고 의미 있는 계층 구조만 유지합니다
    
    3. 반복되는 패턴 처리
       - 리스트 렌더링 등에서 동일한 구조가 반복되는 경우 첫 번째 인스턴스를 기준으로 변환합니다
       - map() 함수 내부의 컴포넌트는 동일한 깊이로 처리합니다
    
    ## 예시
    JSX:
    ```jsx
    <div className={cx('allFrame')}>
      <div className={cx('header')}>
        <div className={cx('test')}>Text</div>
      </div>
    </div>
    
    ## 변경 전 CSS
    ```css
    .allFrame { display: flex; background: red; }
    .header { background: blue; }
    .test { background: yellow; }
    ```
    
    ## 변경 후 CSS
    ```css
    .allFrame { display: flex; background: red; }
    .allFrame .header { background: blue; }
    .allFrame .header .test { background: yellow; }
    ```

     

    4. 확인

    완벽하지 않지만 거의 동일하게 변경되었다.

    앞으로는 변환작업에 신경쓰지 말고 rule 조금씩 손보면서 작업하자!

     

     

    짝짝짝

     

    최대한 rule로 변경할수 있는것 다하고 추가로 작업을 해야하긴하다. 하지만 이거라도 되는게 어디야!!

    일단 여기서 만족하자.. 이제 작업해야해.. 일정이 있자나..ㅠㅠ

    'Language > React' 카테고리의 다른 글

    TypeScript로 MCP Server 만들기  (0) 2025.03.09
    React로 Gauge Chart 만들기  (1) 2025.02.27
    InfiniteScroll 컴포넌트 간단구현  (1) 2025.02.24
    env-cmd란?  (1) 2024.12.06
    react-hook-form을 사용하여 배열 형태 input 사용  (0) 2024.12.02

    댓글

Designed by Tistory.