프로젝트 Next.js 세팅하기
Next.js 세팅
Next.js세팅에 블로그 작성하는 이유는 Next.js 사용해서 프로젝트를 진행하기로 하였다. (참고로 일하면서 진행하다 보니 1주일은 소요되었다....)
이전에 세팅을 해본 적이 있는데 기록을 해두지 않아서 이번 세팅할 때 어려움이 있었다. 이러한 이유와 내가 잘못세팅한 부분을 다른 사용 자들과 공유하고 잘못된 점을 듣고 싶어서 블로그를 작성하게 되었다.
초반 세팅만 되어 있고 폴더 구조는 다음 블로그에서 진행할 예정이다.
사용 스택
코어: Next.js, TypeScript
상태 관리: React-Query, Recoil (차후 세팅 적용 예정)
스타일링: emotion
테스트: jest
빌드: Babel
패키지 매니저: Yarn Berry
next.js에서 제공하는 간편한 초기 세팅 명령어
yarn create next-app --typescript
초기 세팅이기에 몇 가지 설정에 답을 해줘야 한다.
What is your project named? my-app // 앱 이름
Would you like to use TypeScript? No / Yes // 타입스크립트 사용 여부
Would you like to use ESLint? Yes // ESLint 사용 여부
Would you like to use Tailwind CSS? No / Yes // Tailwind 사용 여부
Would you like to use `src/` directory? No // src 폴더 사용 여부
Would you like to use App Router? (recommended) No / Yes // App Router 사용여부
Would you like to customize the default import alias? No / Yes // 커스터마이징 import 사용 여부
질문을 읽고 내가 사용할 것인지 선택만 해주면 된다.
(나는 CSS는 Emotion을 사용할 예정이라 Tailwind는 사용하지 않고 나머지는 사용한다고 했다.)
이것만 하면 NEXT.js는 “기본 세팅”이 끝난다 하지만 우리는 기초로만 작업을 진행하는 경우는 많이 없다 이제 다양한 라이브러리와 린트 설정 등 설정할 것들이 많이 남아있다. 이제 나머지 세팅을 하는 방법을 알아보자.
eslint 세팅
린트 설정을 중요하다. 여러 명이 작업을 하게 되면 모두 각자의 코딩 스타일이 생기게 되는 게 이거는 협업에 가장 큰 어려운 점입니다. 각자 스타일이 다르게 되면 나중에 유지보수에 큰 어려움이 생기게 됩니다.
린트 사용의 이유는 다음과 같습니다.
- 코드 품질 향상 : 린트는 코드의 오류, 버그, 경고, 스타일 문제 등을 찾아내고 수정하는 데 도움이 됩니다. 이를 통해 코드의 품질을 높일 수 있습니다.
- 일관성 유지 : 린트는 개발자들이 일관된 코딩 스타일을 사용하도록 도와줍니다. 이를 통해 코드를 이해하기 쉽고 유지보수하기 쉽게 만들 수 있습니다.
- 협업 향상 : 린트는 모든 개발자들이 동일한 코딩 스타일을 사용하도록 강제함으로써 협업을 간소화합니다. 이를 통해 다른 개발자의 코드를 이해하기 쉽고 협업이 쉽게 만들 수 있습니다.
- 보안 향상 : 린트는 보안 문제를 찾아내는 데 도움이 됩니다. 예를 들어, XSS 공격이나 SQL 삽입 공격과 같은 보안 문제를 찾아낼 수 있습니다.
따라서 린트를 사용하면 코드의 품질을 높이고 일관성을 유지하며 협업을 간소화하고 보안을 향상할 수 있습니다.
. eslintrc.js 파일을 생성하고 다음과 같은 내용을 입력:
module.exports = {
root: true,
// 코드가 실행될 환경을 지정합니다.
env: {
es6: true,
node: true,
browser: true,
},
// 더 나은 유형 검사 및 JSX 지원을 위해 TypeScript 파서를 사용합니다.
parser: "@typescript-eslint/parser",
parserOptions: {
// JSX 구문 분석을 활성화합니다.
ecmaFeatures: { jsx: true },
jsx: true,
// JSX 텍스트 노드 사용을 허용합니다.
useJSXTextNode: true,
},
// 권장 구성 및 플러그인을 확장합니다.
extends: [
"eslint:recommended", // ESLint의 추천 규칙
"plugin:@typescript-eslint/recommended", // 권장 TypeScript 규칙
"eslint-config-prettier", // Prettier에 특화된 ESLint 규칙
"plugin:react/recommended", // 권장 React 규칙
],
// 린팅에 사용되는 추가 플러그인을 지정합니다.
plugins: [
"@typescript-eslint",
"import",
"prettier",
"react",
"react-hooks",
"@emotion",
],
// React 라이브러리 관련 설정을 정의합니다.
settings: { react: { version: "detect" } },
// 사용자 정의 린팅 규칙을 정의합니다.
rules: {
"prettier/prettier": "error", // Prettier 서식을 강제합니다
"no-implicit-coercion": "error", // 암시적 유형 강제 변환을 허용하지 않습니다
"no-undef": "off", // no-undef 규칙을 비활성화합니다
indent: "off", // 들여쓰기 규칙을 비활성화합니다
"@typescript-eslint/indent": "off", // TypeScript 들여쓰기 규칙을 비활성화합니다
semi: "off", // 세미콜론 규칙을 비활성화합니다
"@typescript-eslint/no-non-null-assertion": "off", // 비-Null 단언을 허용합니다
"@typescript-eslint/no-explicit-any": "off", // 명시적 'any' 유형을 허용합니다
"no-extra-boolean-cast": "off", // 추가적인 부울 캐스트를 허용합니다
"getter-return": "warn", // 게터 함수에서 반환이 누락되었을 경우 경고합니다
"@typescript-eslint/explicit-function-return-type": "off", // 추론된 반환 유형을 허용합니다
"@typescript-eslint/no-use-before-define": "off", // 변수를 정의하기 전에 사용하는 것을 허용합니다
"@typescript-eslint/no-empty-interface": "off", // 빈 인터페이스를 허용합니다
"@typescript-eslint/no-parameter-properties": "off", // 매개변수 속성을 허용합니다
// 'util' 모듈에서 특정 경로를 가져오지 못하도록 제한하고 대체 방법을 제안합니다.
"no-restricted-imports": [
"error",
{
paths: [
{
name: "util",
importNames: ["isArray"],
message: "`Array.isArray`를 대신 사용해주세요!",
},
],
},
],
"no-async-promise-executor": "warn", // 비동기 프로미스 실행자 사용에 대한 경고를 표시합니다
"@typescript-eslint/prefer-as-const": "warn", // 상수에 `as const` 사용을 제안합니다
"@typescript-eslint/no-non-null-asserted-optional-chain": "warn", // 선택적 체인에서의 비-Null 단언에 대한 경고를 표시합니다
"@typescript-eslint/ban-types": "warn", // 특정 TypeScript 유형 사용에 대한 경고를 표시합니다
"@typescript-eslint/no-inferrable-types": "warn", // 불필요한 유형 주석에 대한 경고를 표시합니다
"@typescript-eslint/no-empty-function": "off", // 빈 함수를 허용합니다
"@typescript-eslint/naming-convention": [
// 이름 규칙을 강제합니다
"error",
{
format: ["camelCase", "UPPER_CASE", "PascalCase"],
selector: "variable",
leadingUnderscore: "allow",
},
{ format: ["camelCase", "PascalCase"], selector: "function" },
{ format: ["PascalCase"], selector: "interface" },
{ format: ["PascalCase"], selector: "typeAlias" },
],
"@typescript-eslint/explicit-module-boundary-types": "off", // 추론된 모듈 경계를 허용합니다
"@typescript-eslint/array-type": ["error", { default: "array-simple" }], // 일관된 배열 유형 주석을 강제합니다
"@typescript-eslint/no-unused-vars": ["warn", { ignoreRestSiblings: true }], // 사용되지 않은 변수에 대한 경고를 표시합니다
"@typescript-eslint/member-ordering": [
// 일관된 멤버 순서를 강제합니다
"error",
{
default: [
"public-static-field",
"private-static-field",
"public-instance-field",
"private-instance-field",
"public-constructor",
"private-constructor",
"public-instance-method",
"private-instance-method",
],
},
],
"prefer-const": "error", // const 사용을 선호합니다
"no-var": "error", // var 사용을 금지합니다
curly: ["error", "all"], // 중괄호 사용을 강제합니다
eqeqeq: ["error", "always", { null: "ignore" }], // 엄격한 동등성 검사를 요구합니다
"import/no-duplicates": "error", // 중복된 가져오기를 금지합니다
"react/prop-types": "off", // prop-types 요구를 비활성화합니다
"react/display-name": "off", // display-name 요구를 비활성화합니다
"react-hooks/rules-of-hooks": "error", // 훅 규칙을 강제합니다
"react-hooks/exhaustive-deps": "error", // 훅의 exhaustive-deps를 강제합니다
"react/jsx-no-target-blank": "error", // rel="noreferrer" 없이 target="_blank"을 금지합니다
"@typescript-eslint/no-var-requires": "warn", // ES6 가져오기 대신에 `require` 사용에 대한 경고를 표시합니다
"react/react-in-jsx-scope": "off", // react-in-jsx-scope 경고를 비활성화합니다
"react/no-unknown-property": ["error", { ignore: ["css"] }], // React에서 알 수 없는 HTML 속성 사용을 금지합니다
// impor 특정 순서별로 자동 정렬
"import/order": [
"error",
{
groups: [
"builtin",
"external",
"internal",
["parent", "type"],
"sibling",
"index",
"object",
],
},
],
},
};
상단 eslit를 사용하기 위해서는 하단 패키지를 추가해야 한다.
"@types/node": "20.3.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"@emotion/eslint-plugin": "^11.11.0",
"babel-eslint": "^10.1.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
Prettier 세팅
Prettier는 코드 서식을 자동으로 지정해 주는 도구입니다. 이를 사용하면 개발자들이 서로 다른 코딩 스타일을 사용하는 것을 방지할 수 있습니다. 또한, 코드의 가독성을 높이고 일관성을 유지할 수 있습니다.
. prettierrc 파일을 생성하고 다음과 같은 내용을 입력:
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"arrowParens": "avoid"
}
상단 prettier를 사용하기 위해서는 하단 패키지를 추가해야 합니다.
"prettier": "^2.4.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.1.0"
Jest 세팅
Jest는 JavaScript 애플리케이션을 테스트하기 위한 강력한 도구입니다. Jest를 사용하면 테스트를 작성하고 실행하는 데 필요한 모든 기능을 제공합니다.
먼저, 프로젝트의 루트 디렉터리에서 다음 명령어를 실행하여 필요한 패키지를 설치합니다.
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"babel-jest": "^29.6.4",
"@types/jest": "^29.5.1",
"@types/testing-library__jest-dom": "^5.14.5",
"@testing-library/dom": "^9.3.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
이제, 프로젝트 루트 디렉토리에 jest.config.js 파일을 생성하고 다음과 같은 내용을 입력합니다.
module.exports = {
// 파일 변환 설정: Jest에게 어떻게 파일을 변환할지 알려줍니다.
transform: {
'^.+\\\\.[jt]sx?$': ['babel-jest'],
// 정규 표현식을 사용하여 JavaScript 및 TypeScript 파일 (.js, .jsx, .ts, .tsx)을 Babel-Jest를 사용하여 트랜스파일합니다.
},
// 모듈 디렉토리 설정: Jest에게 모듈을 어디에서 찾을지 알려줍니다.
moduleDirectories: ['node_modules', 'src'],
// 모듈을 검색할 디렉토리를 지정합니다. 먼저 'node_modules'를 검색하고, 그 다음 'src' 디렉토리를 검색합니다.
// 모듈 별칭 설정: 파일 확장자에 따라 모듈을 다르게 처리합니다.
moduleNameMapper: {
'\\\\.(css|less|scss|sass)$': 'identity-obj-proxy',
// 정규 표현식을 사용하여 CSS, Less, SCSS, Sass 파일을 가짜 모듈로 대체합니다.
// 이렇게 하면 Jest 테스트에서 CSS 모듈을 쉽게 테스트할 수 있습니다.
},
// Jest 실행 전에 실행할 설정 파일 경로 지정
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
// 테스트 환경 설정: Jest 테스트가 실행될 환경을 지정합니다.
testEnvironment: 'jsdom',
// 'jsdom' 환경을 사용하여 브라우저를 시뮬레이션하는 테스트 환경에서 테스트를 실행합니다.
// 테스트 타임아웃 설정: 각 테스트가 최대로 실행될 수 있는 시간(밀리초)을 설정합니다.
testTimeout: 10000,
// 여기에서는 10,000 밀리초(10초)로 설정되어 있으므로, 한 테스트가 10초 이상 걸리면 실패로 표시됩니다.
};
설정 파일을 만들었으므로, Jest를 사용하여 테스트를 작성하고 실행할 수 있습니다. __tests__/ 디렉토리에 테스트 파일을 작성하고, npm test 명령어를 실행하여 테스트를 실행할 수 있습니다.
Babel 세팅
Babel은 JavaScript 코드를 다양한 환경에서 실행 가능한 형태로 변환해 주는 도구입니다. 다른 JavaScript 버전이나 환경에서 코드를 작성하고자 할 때 Babel을 사용할 수 있습니다. Babel을 설정하는 방법은 프로젝트의 요구사항과 환경에 따라 다를 수 있지만, 일반적인 설정 과정은 다음과 같습니다.
Babel을 사용하기 위해 필요한 패키지를 설치합니다. 대표적인 패키지로는 @babel/core, @babel/preset-env, babel-loader (Webpack에서 사용할 경우), 그리고 다른 필요한 Babel 플러그인이 있습니다.
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.4",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@emotion/babel-plugin": "^11.11.0",
Babel의 설정을 정의하기 위해. babelrc 파일을 프로젝트 루트 디렉터리에 생성합니다. 이 파일은 Babel이 어떻게 코드를 변환해야 하는지에 대한 설정을 담고 있습니다.
. babelrc 파일의 예시:
module.exports = {
plugins: [
'@emotion', // Emotion CSS-in-JS 라이브러리와 함께 사용하기 위한 Babel 플러그인
'@babel/plugin-proposal-optional-chaining', // Optional Chaining 문법을 변환하기 위한 Babel 플러그인
'@babel/plugin-proposal-nullish-coalescing-operator', // Nullish Coalescing Operator 문법을 변환하기 위한 Babel 플러그인
],
presets: [
[
'@babel/preset-react', // React 코드를 변환하기 위한 Babel 프리셋
{
runtime: 'automatic', // React 17에서 도입된 자동 임포트 사용
importSource: '@emotion/react', // Emotion과 함께 사용할 때 React를 어떻게 가져올지 지정
},
],
['@babel/preset-env', { targets: { node: 'current' } }], // 현재 Node.js 버전을 대상으로 하는 Babel 프리셋
'@babel/preset-typescript', // TypeScript 코드를 변환하기 위한 Babel 프리셋
],
};
config-overrides.js 파일은 Create React App (CRA) 프로젝트에서 사용되는 customize-cra 라이브러리를 통해 Babel 설정을 사용자 정의하기 위해 생성하는 파일입니다. 이 파일은 CRA의 기본 Babel 설정을 덮어쓰고 사용자 정의 설정을 적용하기 위해 사용됩니다.
여기에서 주요 이유와 목적을 설명합니다:
- Babel 설정 커스터마이징: CRA는 기본적으로 Babel 설정을 추상화하고 숨기는 경향이 있습니다. 이것은 대부분의 경우에 좋지만, 프로젝트가 특별한 Babel 설정을 필요로 할 때는 사용자 정의가 어려울 수 있습니다. config-overrides.js 파일을 생성하면 CRA의 기본 Babel 설정을 덮어쓸 수 있으므로 사용자 정의 Babel 설정을 쉽게 적용할 수 있습니다.
- 기본 설정 제거: removeBuiltinBabelConfig 함수는 CRA의 기본 Babel 설정을 제거합니다. 이것은 CRA의 Babel 설정을 완전히 비활성화하고 사용자 정의 설정을 사용하도록 하는 중요한 단계입니다. 이렇게 하면 CRA의 기본 설정과 충돌하지 않고 사용자 정의 설정을 적용할 수 있습니다.
- 사용자 정의 설정 활성화: enableBabelConfig 함수는 사용자 정의 Babel 설정 파일인 **babel.config.js**를 활성화합니다. 이렇게 하면 프로젝트의 Babel 설정을 babel.config.js 파일에 정의된 대로 사용할 수 있습니다.
요약하면, config-overrides.js 파일은 Create React App 프로젝트에서 CRA의 기본 Babel 설정을 덮어쓰고 사용자 정의 설정을 적용하기 위한 중요한 역할을 합니다. 이를 통해 프로젝트에서 원하는 Babel 구성을 쉽게 설정하고 사용할 수 있게 됩니다.
. confing-overrides.js 파일의 예시:
/* eslint-disable react-hooks/rules-of-hooks */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const { override, getBabelLoader } = require('customize-cra');
module.exports = override(removeBuiltinBabelConfig, enableBabelConfig);
function removeBuiltinBabelConfig(config) {
const loader = getBabelLoader(config);
loader.options.plugins = [];
loader.options.presets = [];
return config;
}
function enableBabelConfig(config) {
const loader = getBabelLoader(config);
loader.options.configFile = path.resolve(__dirname, 'babel.config.js');
return config;
}
next.config.js 세팅
next.config.js 파일은 Next.js 프로젝트에서 사용되는 환경 및 빌드 구성을 정의하는 파일입니다. 여기에서 주요 설정과 그 목적을 설명합니다:
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// env: {
// API_KEY: 'your-api-key',
// NODE_ENV: process.env.NODE_ENV,
// },
babel: {
presets: ['next/babel'], // Next.js에서 사용하는 Babel 프리셋을 설정
},
typescript: {
ignoreBuildErrors: false, // 타입 오류가 빌드 실패로 이어지도록 수정
},
compiler: {
styledComponents: true, // Next에게 styled-component도 처리해달라고 옵션을 설정해줌
},
images: {
// 이 설정은 어떤 도메인에서 이미지를 불러올 수 있는지를 지정하는 배열입니다.
domains: ['example.com', 'cdn.example.com'],
// 이 설정은 이미지 크기를 지정하는 배열입니다.
// 이는 이미지를 불필요하게 크게 다운로드하지 않고 페이지 로드 성능을 향상시킬 수 있습니다.
imageSizes: [16, 32, 64, 128],
// 이 설정은 정적 이미지를 최적화에서 제외하도록 설정합니다.
// 정적 이미지는 런타임 시 동적으로 생성되는 이미지가 아닌, 빌드 시점에 이미지가 생성되는 경우를 나타냅니다.
disableStaticImages: true,
},
};
module.exports = nextConfig;
tsconfig.json 세팅
tsconfig.json 파일은 TypeScript 프로젝트를 설정하고 제어하는 핵심 도구로서, TypeScript 컴파일러의 동작, 타입 검사, 모듈 시스템, 컴파일러 옵션 및 프로젝트 구성을 정의하는 데 사용됩니다. 이를 통해 TypeScript 프로젝트를 보다 효율적으로 관리하고 안정성을 확보할 수 있습니다.
{
"compilerOptions": {
"target": "es5", // 컴파일된 JavaScript의 대상 버전을 ES5로 설정
"lib": ["dom", "dom.iterable", "esnext"], // 사용 가능한 라이브러리 및 환경을 지정
"allowJs": true, // JavaScript 파일도 컴파일 대상에 포함
"skipLibCheck": true, // 라이브러리 검사 생략 (성능 향상)
"strict": true, // 엄격한 타입 검사 활성화
"forceConsistentCasingInFileNames": true, // 파일 이름의 대소문자 일관성 검사 강제
"noEmit": true, // 컴파일 결과를 생성하지 않음 (TypeScript만 사용)
"esModuleInterop": true, // CommonJS 모듈과 함께 ES 모듈 인터옵(Interop) 활성화
"module": "esnext", // 모듈 시스템을 ESNext로 설정 (import/export 사용)
"moduleResolution": "node", // 모듈 해결 전략을 Node.js 스타일로 설정
"resolveJsonModule": true, // .json 파일을 모듈로 가져오도록 설정
"isolatedModules": true, // 각 파일을 독립 모듈로 컴파일 (성능 향상)
"jsx": "preserve", // JSX 구문을 유지하고 Babel에게 처리 위임
"incremental": true, // 증분 컴파일 활성화 (성능 향상)
"paths": {
"@/*": ["./src/*"] // 모듈 경로 별칭 설정, 예: "@/components"
},
"jsxImportSource": "@emotion/react" // JSX에서 @emotion/react 라이브러리를 가져옴
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], // 컴파일할 파일 목록
"exclude": ["node_modules"] // 컴파일에서 제외할 디렉터리 목록
}
전체 package.json
위에 사용한 package.json 파일을 모아두었다.
"dependencies": {
"@emotion/css": "^11.11.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@next/eslint-plugin-next": "^13.4.7",
"@types/node": "20.3.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"eslint": "8.43.0",
"eslint-config-next": "13.4.7",
"next": "^13.4.7",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.1.3"
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/eslint-plugin": "^11.11.0",
"@types/jest": "^29.5.1",
"@types/testing-library__jest-dom": "^5.14.5",
"@typescript-eslint/eslint-plugin": "^5.60.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.4",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"@testing-library/dom": "^9.3.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"prettier": "^2.8.8"
}
여기까지 불편하지만 봐주신 여러분 감사합니다. ☺️
부족하지만 항상 포기하지 않고 끈질지게 공부해보겠습니다!! 😭