ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [npm] create-react-app 커스텀 템플릿 만들기 (with 타입스크립트)
    Front-end 개발 2024. 3. 18. 23:45

    오늘은 npm 저장소에 나만의 create-react-app (CRA) 커스텀 템플릿을 만들어 본다.

    기존에 타입스크립트 기반의 리액트 프로젝트를 만드는 명령어가 있다.

    // npm 사용자
    npx create-react-app my-app --template typescript
    
    // yarn 사용자
    yarn create react-app my-app --template typescript

     

    멋쟁이사자처럼(멋사) 7기 수업을 받으면서, 수업에 불필요한 요소를 제거하고 필수 요소만 남긴 템플릿을 자주 이용했다. 템플릿 이름은 basic-react이다. 5기였나...? 앞선 기수에서 npm 저장소에 등록해서 사용하면서 사용되기 시작했다. 새로운 프로젝트를 만들 때마다 파일을 삭제 및 정리하는 시간을 없앴다.

     

    요즘은 타입스크립트를 공부하고 있는데 npx로 설치하는데 너무 오랜 시간이 걸리는 것 같다. (yarn으로 설치하던가 해야겠다..) 그래서 이번에는 수업에서 사용했던 basic-react 템플릿을 기반으로 타입스크립트만 덧붙여 새로운 커스텀 템플릿을 npm 저장소에 등록해보고자 한다.

     

    1. 템플릿 프로젝트 생성


    멋사에서 사용했던 가벼운 react 프로젝트 설치(CRA) 명령어는 아래와 같다.

    npx create-react-app [신규 프로젝트명] --template basic-react

     

    템플릿 프로젝트를 생성하기 위해 나는 아래와 같이 프로젝트 명을 템플릿에 맞춰 작명했다.

    생성한 프로젝트는 기존 basic-react 템플릿을 사용했다.

    CRA 탬플릿 프로젝트는 cra-template-[템플릿 이름] 또는 cra-template-typescript-[템플릿 이름] 의 양식을 지켜야 한다.

    npx create-react-app cra-template-typescript-basic-react --template basic-react

     

    먼저 생성해두었던 공식 타입스크립트 문서에 나오는 react CRA 프로젝트 파일 구조를 보면 뭔가 굉장히 많다. 그래서 타입스크립트 기반의 리액트 프로젝트를 만들 때, 불필요한 파일(파비콘, css, png 등)을 제거하고 시작한다. 이번 커스턴 템플릿 등록을 통해서 새로운 프로젝트를 만들 때 반복하는 일을 줄이도록 하겠다.

    기본 타입스크립트 기반 CRA 프로젝트

    멋사 수업에서 사용하는 CRA 프로젝트 파일구조는 src와 public 폴더에서 불필요한 파일을 제거된 상태다.

    basic-react 커스텀 템플릿의 프로젝트 파일구조

    이제 이 프로젝트에 타입스크립트를 설치해서 적용해보자.

     

    2. 프로젝트 커스텀하기


    내가 만들 템플릿은 타입스크립트를 제외한, 추가 라이브러리를 설치하지 않아서 타입스크립트 프로젝트화 하기만 한다. 혹시 react-router-dom, Redux, Recoil, Sass, Tailwind, Styled-component, Mocha 등 추가 라이브러리를 설치해서 자신만의 프로젝트 템플릿을 커스텀 할 수 있겠다.

     

    터미널에서 생성한 프로젝트로 경로를 이동하고, 이미 존재하는 Create React App 프로젝트에 타입스크립트를 추가하기 위해 설치를 해준다. 타입스크립트와 함께 설치해주는 @types/* 는 CRA 프로젝트에서 사용하는 라이브러리들의 타입 선언 파일이 존재하는 패키지들이다.

    // npm 사용자
    npm install --save typescript @types/node @types/react @types/react-dom @types/jest
    
    // yarn 사용자
    yarn add typescript @types/node @types/react @types/react-dom @types/jest

     

    그리고 프로젝트 폴더에 *.js 파일 확장자를 *.tsx로 변경해준다. (예를 들면, src/index.js -> src/index.tsx)

     

    확장자를 변경하면 타입스크립트 컴파일러(tsc)에 의해 발생하는 타입 에러를 고쳐준다.

     

    (1) App.tsx 에러

    JSX를 사용하고 있으므로 기존 코드에 React를 가져오기(import)를 해준다.

    import React from "react"; // 추가
    
    function App() {
      return (
        <div>
          hello world
        </div>
      );
    }
    export default App;

     

    (2) index.tsx 에러
    index.tsx 파일에서 에러가 발생하는데, container 변수에 할당하는 아래와 같이 DOM 엘리먼트에 타입을 단언(Assertion)해주면 해결된다.

    import React from "react";
    import { createRoot } from "react-dom/client";
    import App from "./App";
    
    // const container = document.getElementById("root");
    const container = document.getElementById("root") as HTMLElement; // 타입 단언
    const root = createRoot(container);
    root.render(<App />);

     

    에러를 고쳐주어 에러가 발생한다. (Module not found: Error: Can't resolve './App' in 'E:\ ...생략...')

    타입스크립트 컴파일러가 모듈을 가져오는 방식에 문제가 있는 것 같다.

     

    타입스크립트 컴파일러를 설정 파일을 생성하고, 컴파일러 옵션을 수정해준다.

    // 타입스크립트 컴파일러 설정을 할 수 있는 tsconfig.json 파일 생성
    tsc --init

     

    수정한 컴파일러 옵션은 아래와 같다. 타입스크립트 공식문서에서 리액트 CRA 프로젝트를 생성하면 존재하는 tsconfig.json 파일에서 compilerOptions의 target을 es6(ES2015)로만 수정했다.

    {
      "compilerOptions": {
        "target": "es6",
        "lib": [
          "dom",
          "dom.iterable",
          "esnext"
        ],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "noFallthroughCasesInSwitch": true,
        "module": "esnext",
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "noEmit": true,
        "jsx": "react-jsx"
      },
      "include": [
        "src"
      ]
    }

     

    혹시 VSCode 라이브 서버를 실행 중이었다면 실행 중단 후 재시작을 해준다.

    지금까지 파일 구조는 아래와 같이 변경 됐다.

    커스텀 탬플릿 프로젝트에 수정 및 추가된 파일들

    실행 결과는 정말 심플하다.

    리액트 로고도 없고 아무것도 없이, 그저 hello world 만 찍히 뿐이다.

    기존 basic-react 템플릿 CRA 프로젝트도 결과는 같지만, 달라진 점은 이제 타입스크립트 프로젝트라는 것.

    프로젝트 실행 결과 (VSCode Live Server)

     

    3. 커스텀 템플릿의 파일구조 정리


    파일구조를 아래와 같이 변경해줘야 한다.

    cra-template-[template-name]/
    ├── README.md (for npm)
    ├── template.json
    ├── package.json
    └── template/
        ├── README.md (for projects created from this template)
        ├── gitignore
        ├── public/
        │   └── index.html
        └── src/
            └── index.js (or index.tsx)

     

    새로 생성하는 template 폴더은 Create React App이 설치될 때 사용자의 앱 디렉토리에 복사된다. 이 과정에서 gitignore 파일의 이름은 .gitignore로 변경된다. 위와 같은 파일 구조를 형성하기 위한 작업을 정리하면 아래와 같다.

     

    (1) 프로젝트 최상위 경로에 template 폴더와 template.json 파일 생성

    (2) 프로젝트 최상위 경로의 public, src 폴더와 .gitignore 파일을 template 폴더로 이동

    (3) .gitignore 파일을 마침표(.)를 제거한 gitignore로 변경 (.gitignore -> gitignore)

    (4) template/README.md 파일 생성

    (5) node_modules 폴더와 package-lock.json, tsconfig.json 파일 삭제
    내가 만든 커스텀 템플릿으로 프로젝트 생성 시 src 내부의 .tsx 파일을 감지하고 기본 설정으로 tsconfig.json 파일을 자동 생성함

    (6) 앞서 (1)에서 생성했던 template.json 파일에 package.json 파일의 dependencies, scripts, eslintConfig 부분을 잘라내기 한 뒤, {"package": { 여기 }} 안에 붙여 넣어준다. (복사 붙여넣기가 아니라 잘라내기며, 붙여 넣는 위치 주의!)

    (7) npm에 public 형태로 커스텀 템플릿을 등록할 것이기 때문에 package.json 파일의 "private": true를 제거

     

    // 수정 후 package.json
    {
      "name": "cra-template-typescript-basic-react",
      "version": "0.1.0",
      "private": true,	// npm 저장소에 public으로 배포할 거라면 제거
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      }
    }
    // 수정 후 template.json
    {
        "package": {
            "dependencies": {
                "@testing-library/jest-dom": "^5.17.0",
                "@testing-library/react": "^13.4.0",
                "@testing-library/user-event": "^13.5.0",
                "@types/jest": "^29.5.12",
                "@types/node": "^20.11.28",
                "@types/react": "^18.2.67",
                "@types/react-dom": "^18.2.22",
                "eslint-plugin-jsx-a11y": "^6.8.0",
                "react": "^18.2.0",
                "react-dom": "^18.2.0",
                "react-scripts": "5.0.1",
                "typescript": "^5.4.2",
                "web-vitals": "^2.1.4"
            },
                "scripts": {
                "start": "react-scripts start",
                "build": "react-scripts build",
                "test": "react-scripts test",
                "eject": "react-scripts eject",
                "serve": "serve -s build",
                "build-and-serve": "npm run build && npm run serve"
            },
            "eslintConfig": {
                "extends": [
                "react-app",
                "react-app/jest"
                ]
            }
        }
    }

     

    완성된 파일 구성은 아래 이미지와 같다.

    정리한 파일 구성

     

    4. 로컬 테스트하기


    커스텀 템플릿 프로젝트 구성이 끝났다. 이제 만든 템플릿 패키지을 로컬 다운로드 테스트를 해본다.

     

    커스텀 템플릿 프로젝트 경로에서 벗어나, 아래 명령어로 로컬에서 커스텀 템플릿 다운로드 테스트를 수행할 수 있다.

    // 로컬 커스텀 템플릿 다운로드 명령어
    npx create-react-app [신규 프로젝트명] --template file:/[커스텀 템플릿 폴더 경로]
    
    // 내 로컬 테스트 예시
    npx create-react-app local-test --template file:/user/...생략.../cra-template-typescript-basic-react

     

    정상적으로 설치되고, 커스텀 템플릿 프로젝트가 생성됐다면 성공이다! 👏👏👏

    새로 생성된 프로젝트를 실행하면 template 폴더를 생성해 폴더 구조를 맞춰주기 전처럼 잘 실행된다.

     

    혹시, 아래와 같은 문구가 나오는 MODULE_NOT_FOUND 에러가 발생했는가?

    그렇다면 우선 경로를 제대로 썼는지 확인해보고, 다음으로 경로에 '한글'이 포함되었는지 확인하라.

    한글이 경로에 포함되어 있으면 인식을 못하는 것 같다. (내가 그랬다 🤣)

    const err = new Error(message);
                ^
    Error: Cannot find module 'C:\user\...생략'
    // ...
    // 이런 에러가 발생하면 경로에 한글이 포함되지 않도록 조치하자

     

    +) Removing template pakcage using npm ... 라고하며 npm ERR! 가 많이 출력되는데 결과적으로 성공적으로 컴파일 된다. 호환성의 이유로 타입스크립트 관련 불필요한 템플릿 패키지를 제거하는 작업이라 생각된다. (뒤에 해결함)

    프로젝트를 설치하고 삭제되는 타입스크립트 관련 템플릿 패캐지들

     

    5. npm 저장소에 패키지 배포하기


    (1) 터미널에 npm 계정으로 로그인 한다.

    npm 사이트의 회원가입 절차가 필요하며, 터미널에 로그인 명령어를 치면 OTP(One-Time-Password)를 입력한다.
    OTP는 명령어를 치면 npm 계정 등록시 기입한 이메일 주소로 발송된다.

    npm login
    // Press ENTER to open in the browser ...
    // 이메일로 받은 OTP(One-Time-Password) 입력

     

    명령어 실행 후 ENTER를 누르고 브라우저에 OTP를 넣는다

     

    "Logged in on https://registry.npmjs.org/." 출력되면 로그인 성공! 🎉

     

    (2) npm 저장소에 패키지 배포하기

    터미널의 폴더 경로를 배포할 커스텀 템플릿 폴더로 이동한다.

    // npm 저장소에 public으로 배포
    npm publish --access public
    
    // 배포 성공 시 아래 문구가 나옴
    // npm notice Publishing to https://registry.npmjs.org/

     

     

    만약 아래와 같은 에러가 발생하면 package.json 파일에서 "private":true 라인을 제거한다.

    npm ERR! code EPRIVATE
    npm ERR! This package has been marked as private
    npm ERR! Remove the 'private' field from the package.json to publish it.

     

    배포 성공 (VSCode 터미널)

     

    npm에 로그인하면 등록된 패키지 확인이 가능

     

    (3) 배포한 패키지 다운로드 받기

    커스텀 템플릿 패키지을 다운로드 받는 명령어는 아래와 같다.

    템플릿명에는 프로젝트 폴더명에 붙는 "cra-template-"이 빠져야 함을 주의하자!

    npx create-reate-app [신규 프로젝트명] --template [템플릿명]
    
    // 나의 커스텀 템플릿 다운로드 예시
    npx create-reate-app test-app --template typescript-basic-react

     

    성공적으로 지정한 이름으로 프로젝트가 생성 된다면 성공이다! 🙌

     

    새로 생성된 프로젝트의 폴더 구조도 확인하고, 실행도 한번 해보자!

    나중에 사용할 때 tsconfig.json 설정이 기본이므로, 프로젝트에 맞게 수정하여 사용하면 되겠다.
    (기본 설정도 target 말고는 문제되는 것이 없다.)

    npx 명령어로 생성한 프로젝트 폴더 구조
    실행도 잘 된다

     

    (4) (선택) 재배포

    package.json 파일 버전을 아래 명령어로 업데이트 해준다.

    1.0.1의 버전이 있다고 했을 때, major(주 = 1) / minor(부 = 0) / patch(수 = 1)로 구분된다.

    유의적 버전

     - 기존 버전과 호환되지 않게 API가 바뀌면 "주(主, major) 버전"을 올리고,
     - 기존 버전과 호환되면서 새로운 기능을 추가할 때는 "부(部, minor) 버전"을 올리고,

     - 기존 버전과 호환되면서 버그를 수정한 것이라면 "수(修, patch) 버전"을 올린다.

    npm version patch           // patch 업데이트 (major/minor/patch)
    npm publish --access public // public 배포

     

    나는 npm version patch에서 git 에러가 발생했다.

    npm version patch 결과 (에러)

     

    Git 문제 인것 같아서 git init도 해봤는데 문제는 해결되지 않았다.

    하지만 package.json 파일의 version을 수동으로 바꿔 (0.1.0 -> 0.1.1) 준 뒤 배포를 했더니 성공했다.

    수동으로 수정한 package version
    터미널 상에서 재배포 성공 화면

    (5) 등록한 패키지를 삭제

    나는 패키지 이름이 마음에 안들어서 삭제했다가 다시 등록했다.

    등록한 패키지를 눌러 상세 페이지가 나오면 setting 탭에서 삭제 버튼에 접근할 수 있다.

    npm 사이트의 패키지 삭제 버픈

    6. npm 사이트 README.md 꾸미기 및 의존성 문제 해결


    (1) 패키지 의존성 문제 해결하기

    나는 수업에서 사용하던 basic-react 템플릿 프로젝트(basic-react)를 npm 저장소에서 받아 타입스크립트 설정을 추가 했다. 그 과정에서 최신 버전의 타입스크립트 패키지(@types/*)들이 설치되어 의존성 문제들이 발생했다.

    무수한 npm ERR! 들...

    프로젝트를 설치하고 삭제되는 타입스크립트 관련 템플릿 패캐지들

     

    실행하는데 문제는 없지만, 설치할 때마다 매번 저렇게 문구가 뜨면 아무도 사용하고 싶지 않을 것이다.
    그래서 이미 node_modules는 제거 했으니, 문제는 package 버전 설정에 있다고 생각해서 공식 create-react-app TypeScript 버전의 template.json의 패키지 의존성을 보고 맞춰주었다. 불필요한 명령어도 제거해줬다.

    (공식 CRA가 업데이트 되면 내 패키지도 맞춰서 업데이트 시켜줘야 겠네...?)

    서로 다른 패키지 의존성 및 명령어

    최종적인 template.json 파일은 아래와 같다.

    {
        "package": {
            "dependencies": {
                "@testing-library/jest-dom": "^5.17.0",
                "@testing-library/react": "^13.4.0",
                "@testing-library/user-event": "^13.5.0",
                "@types/jest": "^27.5.2",
                "@types/node": "^16.18.89",
                "@types/react": "^18.2.66",
                "@types/react-dom": "^18.2.22",
                "eslint-plugin-jsx-a11y": "^6.8.0",
                "react": "^18.2.0",
                "react-dom": "^18.2.0",
                "react-scripts": "5.0.1",
                "typescript": "^4.9.5",
                "web-vitals": "^2.1.4"
            },
                "scripts": {
                "start": "react-scripts start",
                "build": "react-scripts build",
                "test": "react-scripts test",
                "eject": "react-scripts eject"
            },
            "eslintConfig": {
                "extends": [
                "react-app",
                "react-app/jest"
                ]
            }
        }
    }

     

    이제 수정한 프로젝트의 버전을 수정해주고 재배포를 해주면 적용이 된다.

    // package.json 수정. patch 부분만 변경해 줬다.
    {
        "version": "0.1.2"
    }
    
    // 터미널 npm 로그인 및 배포 명령어
    npm login
    npm publish --access publish

     

    이제 npx 명령어로 다시 패키지를 설치해 보면 의존성 관련된 npm 메시지가 제거 됐다.

    많던 npm ERR!가 없어졌다

     

    (2) npm 사이트에 보여지는 README.md 파일 꾸미기

    기존에 존재하던 README.md 파일의 내용이 나의 패키지를 설명하는 페이지에 나오는데 의미가 없어보인다. 그래서 README.md 파일을 담백하게 꾸미기로 했다. 기존 내용은 내가 생성하고 비워두었던 template 폴더의 REAME.md 폴더에 잘라 붙여넣기를 해줬다. 이렇게 해주면 새로운 프로젝트를 만들 때 생겨나는 REAME 파일의 내용이 채워지게 된다. 나는 딱히 설치하고 설명을 붙여줄 내용이 없어서 채워두기만 했다. 설치하고 사용하는 사람을 위해 도움을 주고 싶다면 써주자.

    최상위 경로의 REAME.md는 npm 사이트의 패키지 설명의 간판이므로 정말 필요한 내용을 작성하고 제거했다.

    최상위 경로의 REAMD.md 파일 수정

     

    npm 패키지 상세페이지를 보면 제일 하단에 Keyword가 있는데 이 부분은 REAME.md 파일이 아닌 최상위 폴더의 package.json에서 "keywords": [ 여기 ] 배열 형태로 작성한다.

    // package.json
    {
      "name": "cra-template-typescript-basic-react",
      "version": "0.1.3",
      "description" : "This is TypeScript template for basic-react App",
      "keywords": ["react", "create-react-app", "typescript","custom template", "basic-react"],
      // ...
      }
    }

     

    REAME.md (내용) 파일과 pakcage.json (키워드)를 수정한 결과, 상세 페이지는 아래와 같이 깔끔해졌다.

     

    수정된 npm 패키지 상세 페이지

     

    (3) 패키지 상세 페이지에 GitHub 저장소 정보 남기기
    다른 패키지들을 보니까 GitHub 저장소에 올리고 링크도 게시해 두었길래 나도 추가해본다.

    GitHub 레포지토리를 후다닥 생성하고, 새로 생성한 파일에 git clone, 프로젝트 넣고 git add > commit > push

    키워드를 추가했던 방식과 동일하게 package.json 파일을 수정한다.
    새로운 속성으로 "repository"를 추가하면 되는데, 나는 "author" 속성도 넣어주었다.

    {
      "name": "cra-template-typescript-basic-react",
      "version": "0.1.4",
      "description" : "This is TypeScript template for basic-react App",
      "keywords": ["react", "create-react-app", "typescript","custom template", "basic-react"],
      "repository": {
        "type": "git",
        "url": "https://github.com/redcontroller/npm-cra-template.git"
      },
      "author": "redcontorller",
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      }
    }

     

    다시 터미널을 통해 npm login을 하고, npm publish --access publish 명령어로 배포해준다.

    GitHub 저장소 링크가 추가 됐다

     

     

     

    헷 성공적이다 😎

     

    끝...

     

     

    Reference


    원본 자료: 승연 기록장

     

    CRA Custom Template 만들고 npm에 모듈 배포하기

    어쩌다 만들게 됐나요? 리액트를 공부하고 있는 요즘.. CRA 프로젝트를 생성할때마다 필요없는 파일을 삭제하는 과정이 너무 귀찮다고 생각하던 와중에 멋사 수강생 분께서 CRA 프로젝트를 커스

    sypear.tistory.com

    참고 자료 1. Create React App 공식 문서 - Adding TypeScript

     

    Adding TypeScript | Create React App

    Note: this feature is available with react-scripts@2.1.0 and higher.

    create-react-app.dev

    참고 자료2. Create React App 공식 문서 - Custom Templates

    참고 자료3. 메쉬코리아 웹 프론트엔드 블로그

    참고 자료4. npm 저장소에 패키지 저장하기

     

     

Designed by Tistory.