레거시 프로젝트에서 작업을 하게 되었다. 작업 후, feature 환경으로 CICD 진행 중 린트 오류가 발생했다.
로컬에서는 아무 문제 없이 돌아가는데, CICD 빌드에서 prettier/prettier 린트 에러가 발생했다.
이 문제를 해결하는데 생각보다 시간을 많이 소비했다.
'수정 -> 로컬에서 빌드 -> CI 빌드 -> 오류 확인' 의 과정에서 빌드에서 많은 시간을 소비했다.
그래서 글로 작성해두려고 한다.
[eslint]
/builds/xxxx/xxxxx/src/common/constants/보안등급.ts
15:22 error Replace `(typeof·보안등급)` with `typeof·보안등급` prettier/prettier
/builds/xxxx/xxxxx/src/common/constants/보존연한.ts
9:22 error Replace `(typeof·보존연한)` with `typeof·보존연한` prettier/prettier
/builds/xxxx/xxxx/src/components/dialog/attendance/dayRules/type.ts
6:24 error Replace `(typeof·REST_TYPE)` with `typeof·REST_TYPE` prettier/prettier에러 내용을 보면 (typeof X)에서 괄호를 제거하라고 하는데, 로컬에서는 반대로 괄호를 추가하려고 했다!
문제의 핵심은 로컬과 CICD 환경의 Prettier 버전이 달랐다
// package.json
"devDependencies": {
"eslint-config-prettier": "^6.12.0",
"eslint-plugin-prettier": "^3.1.4",
// prettier 버전이 명시되어 있지 않음! ❌
}Prettier가 package.json에 명시되어 있지 않아서:
로컬: 오래된 Prettier 버전이 캐시로 남아있음 → 괄호를 추가하려고 함
CICD: npm ci로 clean install → 최신 Prettier 설치 → 괄호를 제거하려고 함
Prettier 3.0부터 TypeScript의 typeof 연산자 처리 방식이 변경되었다.
구버전 (< 3.0):
// 괄호를 추가함 (명확성을 위해)
export type 보안등급유형 = (typeof 보안등급)[number]['value'];
신버전 (>= 3.0):
// 불필요한 괄호를 제거함
export type 보안등급유형 = typeof 보안등급[number]['value'];
추가로 .eslintrc.js에서 prettier 관련 설정이 중복되어 있었다.
// ❌ 중복된 설정
extends: [
'plugin:vue/essential',
'eslint:recommended',
'@vue/typescript',
'prettier', // 중복!
'plugin:prettier/recommended' // 이미 prettier 포함
],
plugins: ['prettier'], // 불필요!plugin:prettier/recommended가 이미 prettier 설정과 플러그인을 모두 포함하고 있어서 중복 선언이 필요 없었다.
// .eslintrc.js
extends: [
'plugin:vue/essential',
'eslint:recommended',
'@vue/typescript',
'plugin:prettier/recommended' // 이것만으로 충분
],
// plugins: ['prettier'], // 제거CICD 환경(신버전 Prettier)에 맞춰 6개 파일의 괄호를 제거
// Before
export type 보안등급유형 = (typeof 보안등급)[number]['value'];
export type 보존연한유형 = (typeof 보존연한)[number]['value'];
export type RestType = (typeof REST_TYPE)[keyof typeof REST_TYPE];
type ALARM_TARGET_TYPE = (typeof ALARM_TARGET)[keyof typeof ALARM_TARGET];
type HOLIDAY_REPEAT = (typeof HOLIDAY_REPEAT_TYPE)[keyof typeof HOLIDAY_REPEAT_TYPE];
export type TIME_UNIT = (typeof TIME_UNIT)[keyof typeof TIME_UNIT];
// After
export type 보안등급유형 = typeof 보안등급[number]['value'];
export type 보존연한유형 = typeof 보존연한[number]['value'];
export type RestType = typeof REST_TYPE[keyof typeof REST_TYPE];
type ALARM_TARGET_TYPE = typeof ALARM_TARGET[keyof typeof ALARM_TARGET];
type HOLIDAY_REPEAT = typeof HOLIDAY_REPEAT_TYPE[keyof typeof HOLIDAY_REPEAT_TYPE];
export type TIME_UNIT = typeof TIME_UNIT[keyof typeof TIME_UNIT];환경 정보:
Vue 2.7.0
TypeScript 4.8.2
ESLint 7.32.0
Prettier 3.0+ (CICD)