리액트 애플리케이션을 쉽게 만들기 위한 CLI 도구인
create-react-app 은 지속적인 업데이트가 되지 않을 수도 있다
package.json 을 직접 만들 수도 있지만
npm init
을 통해 기본적인 package.json 을 만들 수 있다
JSON 최상단에 $schema 를 다음과 같이 작성하면
scheamaStore 에서 JSON 파일이 무엇을 의미하고 어떤 키와 값이 들어갈 수 있는지 정보를 제공한다
자동완성도 제공하니 사용하면 좋을 것 같다
{
"$schema": "https://json.schemastore.org/tsconfig", // 이 파일의 구조를 검증하기 위한 JSON 스키마 URL.
"compilerOptions": {
"target": "ES5", // 컴파일된 JavaScript의 목표 버전 (ECMAScript 5).
"lib": ["dom", "esnext", "DOM.Iterable"], // 포함할 라이브러리들 (DOM, 최신 ECMAScript, DOM.Iterable).
"allowJs": true, // JavaScript 파일을 TypeScript 프로젝트에서 허용.
"skipLibCheck": true, // 타입 정의 파일의 검사를 건너뜀.
"strict": true, // 모든 엄격한 타입-체킹 옵션을 활성화.
"forceConsistentCasingInFileNames": true, // 파일 이름의 대소문자 일관성 강제.
"noEmit": true, // 컴파일된 파일을 생성하지 않음.
"esModuleInterop": true, // ES 모듈과 CommonJS 모듈 간의 호환성을 추가.
"module": "esnext", // 모듈 시스템 설정 (최신 ECMAScript 모듈).
"isolatedModules": true, // 모든 파일을 개별 모듈로 처리.
"jsx": "preserve", // JSX를 변환하지 않고 그대로 유지.
"incremental": true, // 증분 컴파일을 활성화하여 컴파일 시간을 단축.
"baseUrl": ".", // 기본 경로 설정.
"paths": {
"@/*": ["src/*"] // 경로 별칭 설정. "@/*"는 "src/*"로 매핑.
},
"outDir": "./dist", // 컴파일된 파일이 저장될 디렉터리.
"noImplicitAny": false, // 암묵적인 "any" 타입 사용을 허용.
"noUnusedLocals": true, // 사용되지 않는 지역 변수를 오류로 표시.
"noUnusedParameters": true // 사용되지 않는 매개변수를 오류로 표시.
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], // 포함할 파일들.
"exclude": ["node_modules", "dist"] // 제외할 파일들.
}
타입스크립트를 자바스크립트로 컴파일 할 때 사용하는 옵션
target: “ES5”
• JavaScript 출력 버전을 ES5로 설정, 광범위한 브라우저 호환성 제공
lib: “dom”, “esnext”, “DOM.Iterable”
• 컴파일에 포함될 라이브러리 파일 지정
• “dom”: 브라우저 DOM API
• “esnext”: 최신 ECMAScript 기능
• “DOM.Iterable”: DOM 컬렉션의 반복 가능성
allowJs: true
• JavaScript 파일의 컴파일 허용
skipLibCheck: true
• 선언 파일(.d.ts)의 타입 검사를 건너뛰어 컴파일 시간 단축
strict: true
• 모든 엄격한 타입 검사 옵션 활성화
forceConsistentCasingInFileNames: true
• 파일 이름의 대소문자 일관성 강제
noEmit: true
• 컴파일 결과물 생성하지 않음 (타입 검사만 수행)
esModuleInterop: true
• CommonJS 모듈을 ES6 모듈처럼 가져올 수 있게 함
module: “esnext”
• 최신 ECMAScript 모듈 시스템 사용
isolatedModules: true
• 각 파일을 별도의 모듈로 트랜스파일
jsx: “preserve”
• JSX를 그대로 유지하여 다른 도구가 처리할 수 있게 함
incremental: true
• 이전 컴파일의 정보를 저장하여 증분 컴파일 가능
baseUrl: “.”
• 비 상대적 모듈 이름을 해석하기 위한 기본 디렉토리를 현재 디렉토리로 설정
paths: {”@/”: “src/”}
• 모듈 이름 재매핑 설정 (”@/“로 시작하는 임포트를 “src/” 디렉토리로 매핑)
outDir: “./dist”
• 컴파일된 파일의 출력 디렉토리를 “dist”로 지정
noImplicitAny: false
• 암시적 ‘any’ 타입에 대한 오류 보고 비활성화
noUnusedLocals: true
• 사용되지 않는 지역 변수에 대한 오류 보고
noUnusedParameters: true
• 사용되지 않는 매개변수에 대한 오류 보고
include: “next-env.d.ts”, “/.ts”, “/*.tsx”
• 컴파일에 포함될 파일 패턴 지정 (Next.js 환경 선언 파일과 모든 TypeScript 및 TSX 파일 포함)
exclude: “node_modules”, “dist”
• 컴파일에서 제외할 디렉토리 지정 (“node_modules”와 “dist” 디렉토리 제외)
next.config.js 는 버전별로 달라지므로
next 깃허브에서 버전에 맞게 사용 가능한 옵션을 찾아보아야 한다
reactStrictMode: 리액트 엄격 모드 활성화
powerdByHeader: 보안 취약점으로 취급되는 X-Powered-By 헤더 제거
eslint.ingnoreDuringBuilds: 빌드 시 ESLint 제외, 일반적으로는 빌드 시 포함됨
구글에서 제공하는 액션으로 라이트하우스를 CI 기반으로 실행할 수 있도록 도와주는 도구
의존성에 문제가 있을 때 알려주고
가능하면 해결할 수 있는 풀 리퀘스트까지 열어준다
Dependabot 을 활용해 의존성의 보안 위협을 제거할 수도 있다
프로젝트에 사용되는 dependencies 중 보안 이슈가 발생한 패키지에 관한 정보를 알려주고 자동으로 버전을 올려주는 작업까지 수행 할 수 있다
https://github.com/dependabot
repository의 Setting의 Code security and analysis 탭에서 쉽게 Enable 가능하다
유의적 버전(semantic versioning) 에 대해 먼저 알아야 한다
유의적 버전
버전은 주.부.수 로 구성돼 있으며
각각의 정의는 다음과 같다
1. 기존 버전과 호환되지 않게 API 가 바뀌면 주 버전을 올린다
2. 기존 버전과 호환되면서 새로운 기능을 추가할 때는 부 버전을 올린다
3. 기존 버전과 호환되면서 버그를 수정했다면 수 버전을 올린다
특정 버전으로 패키지를 배포하고 나면 그 버전의 내용은 절대 변경되지 않아야 한다
변경사항이 있을 때는 항상 버전을 올려야 한다
주버전이 0인 경우에는 초기 개발을 위한 단계로 아무 때나 마음껏 바꿀 수 있다
react@16.0.0: 정확시 16버전에 호환되는 경우
react@^16.0.0: 16.0.0 부터 17.0.0 미만의 모든 버전을 호환한다는 뜻
react@~16.0.0: 패치 버전에 대해서만 호환되는 버전을 의미한다 16.0.0 부터 16.1.0 미만의 모든 버전
유의적 버전은 개발자들의 약속일 뿐 그 것이 실제로 지켜졌는지는 알 수 없다
의존성이란 프로젝트를 운영하는 데 필요한 자신 외의 라이브러리를 정의해 둔 목록이다
package.json 은 주로 dependencies 와 devDependencies 로 구성돼 있다
devDependencies 는 개발 단계의 의존성
peerDependencies 는 주로 서비스보다는 라이브러리와 패키지에서 자주 쓰인다
직접적으로 해당 패키지를 import 하지 않지만 호환성으로 인해 필요한 경우 작성한다
최근에는 애플리케이션 실행에 필요한 패키지를 구분하는 것에 의문을 제기하는 경우도 있다
첫 번째 이유는 번들러
dependencies 에 있든 devDependencies 에 있든
node_modules 에 설치되는 것은 같다
여기서 실제 서비스에 배포할지를 결정하는 것은 번들러이다
두 번쨰 이유는 복잡해진 개발 파이프라인이다
과거에는 이렇게 구분하여 설치하는 전략이 주효했지만
과거 방식으로는 현재 빌드조차 할 수 없는 경우가 있다
npm 에 업로드할 패키지를 개발한다면 구분이 중요하다
dependabot이 문제점을 발견하면 Security 탭의 Dependabot alerts에 issue를 추가해 주며 repository 메인 화면에서도 다음과 같이 알림창을 띄워주는 것을 확인 할 수 있다
생성된 issue를 확인하면 어떠한 종속성 패키지의 어떤버전에서 보안 이슈가 발생했으며 그 보안 이슈의 위험성 및 어떠한 종류인지 상세하게 설명해 준다
CWE에 등록된 보안 이슈들을 기준으로 명세되며 어떠한 버전으로 업그레이드 해야하는지 알려준다
버튼 하나로 보안 이슈가 발생하지 않는 패키지의 버전 업그레이드 PR을 생성해준다
버튼 하나만 누르면 모든 의존성 보안 이슈를 처리해주는것처럼 설명하였지만 생각보다 잘작동하지 않는 경우가 많이 발생했다
가장 흔한 케이스는 의존성의 의존성에서 문제가 발생했을 경우이다
Dependabot 으로 수정하기 어려운 이슈라면 npm 의 overrides 를 활용하면 좋다
만들어진 배경이 보안 이슈 수정에 있어, 오래되거나 문제가 있는 의존성을 집어서 수정하는데 유용하다
vercel 등으로 배포하는 방식은 쉽고 빠르다
트래픽이 적은 개인 프로젝트나 테스트용으로 적합하지만 사용자에게 서비스하기 위한 웹 애플리케이션을 적용하기에는 적절하지 않을 수 있다
커스터마이징에도 제약이 있다
때문에 직접 서버에 배포해야 하는데 이 때 컨테이너로 만들어 빠르게 배포하는 것이 일반적이다
애플리케이션을 도커 이미지로 만드는 과정을 도커라이즈라고 한다
애플리케이션을 신속하게 구축해 배포할 수 있는 상태로 준비하는 것이라고 할 수 있다
도커는 개발자가 모던 애플리케이션을 구축, 공유, 실행하는 것을 도와줄 수 있도록 설계된 플랫폼으로 지루한 설정 과정을 대신해주므로 코드 작성에만 집중할 수 있다
도커는 빠른 배포를 위해 애플리케이션을 컨테이너라는 단위로 패키징하고
컨테이너 내부에서 애플리케이션을 실행할 수 있도록 도와준다
컨테이너를 바탕으로 독립된 환경에서 애플리케이션이 항상 일관되게 실행될 수 있도록 보장해준다
이름: 태그명
형태로 구성돼 있다 (ubuntu: latest)대부분의 cli 명령어는 도커 테스크톱의 GUI 인터페이스에서 제공하는 기능이다
프론트엔드 애플리케이션이 도커 이미지에서 해야 할 작업을 간단히 요약하면 5단계가 있다
1. 운영체제 설정
2. Node.js 설치
3. npm ci
4. npm run build
5. 실행
이 과정들을 Dockerfile 에 작성
FROM node:18.12.0-alpine3.16 as build
WORKDIR /app
COPY package.json ./package.json
COPY package-lock.json ./package-lock.json
RUN npm ci
COPY . ./
RUN npm run build
FROM node:18.12.0-alpine3.16 as build
는 이미지가 어떤 베이스 이미지에서 실행될지를 결정한다 도커는 다른 이미지 위에 이미지를 생성할 수 있다
node 가 설치되어 있는 이미지 위에서 실행할 것을 의미한다
alpine 은 리눅스를 의미하는데 일반적인 리눅스와 달리 씬 가벼운 리눅스로 컨테이너를 실행하기 위한 운영체제로 주로 사용된다
이 것들은 도커 허브에서 가져오는 것으로 as build
는 build 라는 단계의 스테이지에서만 쓰겠다는 의미
WORKDIR 설정 후
필요한 의존성을 COPY 후 설치,
npm run build 를 실행한다
여기까지가 리액트 빌드에 필요한 Dockerfile 이다
docker build . -t cra:test
명령을 입력하면 해당 위치에서 빌드를 수행, -t 옵션으로 cra:test 라는 이름과 태그를 부여하겠다는 뜻이다
build 후 보면 node.js 만 실행된 것을 볼 수 있다
node.js 가 실행된 이유는 node:18.12.0-alpine3.16
이미지를 보면 알 수 있다
node:18.12.0-alpine3.16 의 Dockerfile 마지막에 CMD ["node"]
가 있기 때문에 node.js 가 실행된 것이다
원하는 것이 node.js 가 실행되고 애플리케이션이 빌드되는 것이 아니기 때문에
빌드 후 실행을 위해서는 다음과 같이 설정을 추가해줘야 한다
FROM nginx:1.23.2-alpine as start
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 3000
ENTRYPOINT ["nginx", "-g", "daemon off;"]
FROM nginx:1.23.2-alpine as start
빌드된 정적 파일을 서비스하기 위해 최신 버전의 nginx 가 설치된 알파인 리눅스를 설치
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
빌드한 파이릉ㄹ nginx 가 서비스할 수 있도록 nginx 설정 파일 복사
COPY --from=build /app/build /usr/share/nginx/html
앞서 설정한 build 단계에서 app/build 만 가져와 현재 단계인 start 의 원하는 위치인 /usr/share/nginx/html 에 복사한다
필요한 리소스만 가져와 start 단계에서 사용할 수 있게 된다
EXPOSE 3000 으로 열어준 포트는 도커 이미지를 실행할 때 호스트 운영체제에서 오픈된다
ENTRYPOINT: 컨테이너 시작 시 어떤 명령을 실행할지 결정한다
Dockerfile 내에서 한 번만 실행할 수 있다
여기서는 nginx 의 데몬을 시작하도록 한다
이렇게 실행하면 이전에 비해 도커 이미지가 훨씬 작아지고
이미지 실행을 위해 앞서 정의한 3000번 포트를 열어야 한다
이미지 실행 후 http://localhost:3000/
으로 접근하면 애플리케이션이 nginx 를 통해 서비스 되는 것을 확인할 수 있다
react 와 전체 과정은 비슷하다