API를 사용하는 팀에게 일관성, 품질 및 설계 표준 준수는 매우 중요합니다. OpenAPI 및 AsyncAPI와 같은 API 사양은 청사진을 제공하지만, 수많은 서비스와 팀에 걸쳐 이러한 청사진이 올바르게 준수되도록 하는 것은 어려운 과제가 될 수 있습니다. 이때 API 린팅 도구가 등장하며, Spectral은 유연하고 강력한 오픈 소스 옵션으로 두드러집니다. TypeScript와 결합될 때, Spectral은 개발자가 강력하고 타입 안전한 사용자 지정 규칙을 만들 수 있도록 지원하여 API 거버넌스를 새로운 수준으로 끌어올립니다.
이 튜토리얼은 초기 설정부터 정교한 사용자 지정 유효성 검사 로직 작성까지, TypeScript와 함께 Spectral을 활용하는 과정을 안내합니다. TypeScript가 Spectral 규칙 개발을 어떻게 향상시켜 더 유지보수 가능하고 신뢰할 수 있는 API 린팅 솔루션을 제공하는지 살펴보겠습니다.
개발 팀이 최고의 생산성으로 함께 작업할 수 있는 통합된 올인원 플랫폼을 원하시나요?
Apidog는 귀하의 모든 요구를 충족하며, 훨씬 저렴한 가격으로 Postman을 대체합니다!
Spectral 이해하기: API 사양의 수호자
TypeScript 통합에 대해 알아보기 전에 Spectral이 무엇이며 왜 API 개발 툴킷에서 가치 있는 도구인지 알아보겠습니다.
Spectral은 OpenAPI (v2 및 v3) 및 AsyncAPI와 같은 API 설명 형식에 주로 초점을 맞춘 오픈 소스 JSON/YAML 린터입니다. 그 목적은 API 설계 가이드라인을 적용하고, 일반적인 오류를 감지하며, API 환경 전반에 걸쳐 일관성을 보장하는 데 도움을 주는 것입니다. API 계약을 위한 ESLint 또는 TSLint라고 생각하시면 됩니다.
Spectral 사용의 주요 이점:
- 일관성: 팀 및 프로젝트 전반에 걸쳐 균일한 API 설계를 적용합니다.
- 품질 보증: 개발 라이프사이클 초기에 오류 및 잘못된 관행을 포착합니다.
- 향상된 협업: API 표준에 대한 공유된 이해를 제공합니다.
- 자동화: 자동화된 유효성 검사를 위해 CI/CD 파이프라인에 원활하게 통합됩니다.
- 확장성: 특정 조직 요구에 맞춰 사용자 지정 규칙을 생성할 수 있습니다.
- 형식에 구애받지 않는 코어: OpenAPI/AsyncAPI에서 뛰어나지만, 코어는 모든 JSON/YAML 구조를 린트할 수 있습니다.
Spectral은 규칙 세트(rulesets)를 기반으로 작동합니다. 규칙 세트는 규칙 모음이며, 각 규칙은 API 문서의 특정 부분(JSONPath 표현식 사용)을 대상으로 유효성 검사 로직을 적용합니다. Spectral은 내장 규칙 세트(예: OpenAPI 표준을 위한 spectral:oas
)와 함께 제공되지만, 진정한 힘은 사용자 지정 규칙 세트를 정의하는 능력에 있습니다.
Spectral 사용자 지정 규칙에 TypeScript를 사용하는 이유는 무엇인가요?
Spectral 규칙 세트는 YAML 또는 JavaScript(.js
파일)로 정의할 수 있지만, 사용자 지정 함수 개발에 TypeScript를 사용하면 상당한 이점을 얻을 수 있습니다.
- 타입 안전성: TypeScript의 정적 타이핑은 컴파일 시점에 오류를 포착하여 사용자 지정 린팅 로직의 런타임 예기치 않은 상황을 줄입니다. 이는 복잡한 규칙에 매우 중요합니다.
- 향상된 개발자 경험: IDE에서 자동 완성, 리팩토링 기능 및 더 나은 코드 탐색 기능은 사용자 지정 함수를 더 쉽게 작성하고 유지 관리할 수 있도록 합니다.
- 향상된 가독성 및 유지보수성: 명시적인 타입은 사용자 지정 함수의 의도와 구조를 명확하게 하여, 특히 팀에게 유용합니다.
- 최신 JavaScript 기능: TypeScript는 호환 가능한 JavaScript로 컴파일되므로 최신 ES 기능을 자신 있게 활용할 수 있습니다.
- 더 나은 테스트 가능성: 타이핑은 사용자 지정 Spectral 함수에 대한 강력한 단위 테스트를 더 쉽게 작성할 수 있도록 합니다.
사용자 지정 Spectral 함수를 TypeScript로 작성함으로써, 애플리케이션 코드에 적용하는 것과 동일한 엄격함과 도구의 이점을 API 거버넌스 코드에도 가져올 수 있습니다.
Spectral 및 TypeScript 환경 설정하기
이제 직접 해보면서 필요한 도구를 설정해 보겠습니다.
필수 구성 요소:
- Node.js 및 npm (또는 yarn): Spectral은 Node.js 애플리케이션입니다. Node.js (LTS 버전 권장) 및 npm (또는 yarn)이 설치되어 있는지 확인하세요.
- TypeScript 프로젝트: TypeScript 프로젝트가 필요하거나 설정할 의향이 있어야 합니다.
설치 단계:
먼저 린팅 작업을 실행하고 규칙을 테스트하기 위해 Spectral CLI가 필요합니다. 전역으로 설치하거나 npx
를 사용하는 것이 유용합니다.
Bash
npm install -g @stoplight/spectral-cli
# or
yarn global add @stoplight/spectral-cli
사용자 지정 규칙을 프로그래밍 방식으로 개발하고 TypeScript 프로젝트 내에서 Spectral의 코어 라이브러리를 사용하려면 필요한 Spectral 패키지를 설치하세요:
Bash
npm install @stoplight/spectral-core @stoplight/spectral-functions @stoplight/spectral-rulesets typescript ts-node --save-dev
# or
yarn add @stoplight/spectral-core @stoplight/spectral-functions @stoplight/spectral-rulesets typescript ts-node --dev
이 패키지들을 자세히 살펴보겠습니다:
@stoplight/spectral-core
: 린팅 엔진을 포함하는 Spectral의 핵심입니다.@stoplight/spectral-functions
: 규칙이 사용할 수 있는 내장 함수 모음(예:alphabetical
,defined
,pattern
,truthy
,xor
)을 제공합니다.@stoplight/spectral-rulesets
:spectral:oas
(OpenAPI용) 및spectral:asyncapi
와 같은 미리 정의된 규칙 세트를 제공합니다.typescript
: TypeScript 컴파일러입니다.ts-node
: 사전 컴파일 없이 TypeScript 파일을 직접 실행할 수 있게 해줍니다. 개발에 유용합니다.
TypeScript 구성:
아직 tsconfig.json
파일이 없다면 프로젝트 루트에 생성하세요. 기본 구성은 다음과 같을 수 있습니다:
JSON
{
"compilerOptions": {
"target": "es2020", // Or a newer version
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist", // Output directory for compiled JavaScript
"rootDir": "./src", // Source directory for your TypeScript files
"resolveJsonModule": true // Allows importing JSON files
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
프로젝트 구조에 따라 outDir
및 rootDir
를 조정하세요. 사용자 지정 TypeScript 함수는 src
디렉토리에 위치한다고 가정하겠습니다.
Spectral 핵심 개념: 규칙, 규칙 세트 및 함수
TypeScript 함수를 작성하기 전에 Spectral의 주요 구성 요소에 대한 이해를 확고히 합시다.
규칙:
규칙은 수행할 특정 검사를 정의합니다. 규칙의 주요 속성은 다음과 같습니다:
description
: 사람이 읽을 수 있는 규칙 설명입니다.message
: 규칙 위반 시 표시할 오류 또는 경고 메시지입니다.{{error}}
,{{path}}
,{{value}}
와 같은 플레이스홀더를 포함할 수 있습니다.severity
: 규칙 위반의 영향을 정의합니다.error
,warn
,info
, 또는hint
가 될 수 있습니다.given
: 규칙이 적용될 문서의 특정 부분(또는 그 배열)을 지정하는 JSONPath 표현식입니다.then
: 대상 값에 대해 취할 작업을 정의합니다. 일반적으로 하나 이상의 함수를 적용하는 것을 포함합니다.function
: 실행할 내장 또는 사용자 지정 함수의 이름입니다.functionOptions
: 함수에 전달할 옵션입니다.formats
: 이 규칙이 적용될 문서 형식(예:oas3
,oas2
,asyncapi2
)을 지정하는 배열입니다.
규칙 세트:
규칙 세트는 YAML 또는 JavaScript 파일(예: .spectral.yaml, .spectral.js 또는 컴파일 시 .spectral.ts)로 규칙을 그룹화합니다. 또한 다음을 수행할 수 있습니다:
extends
: 다른 규칙 세트(예: 내장 Spectral 규칙 세트 또는 공유 조직 규칙 세트)에서 규칙을 상속합니다.rules
: 사용자 지정 규칙 정의를 포함하는 객체입니다.functionsDir
: 사용자 지정 JavaScript 함수 파일이 위치한 디렉토리를 지정합니다.functions
: 사용자 지정 함수 배열입니다 (functionsDir
또는 프로그래밍 방식 설정을 사용할 때는 덜 일반적입니다).
함수:
함수는 실제 유효성 검사를 수행하는 핵심 로직 단위입니다. Spectral은 다음과 같은 많은 내장 함수를 제공합니다:
truthy
: 값이 truthy인지 확인합니다.falsy
: 값이 falsy인지 확인합니다.defined
: 속성이 정의되었는지 확인합니다.undefined
: 속성이 undefined인지 확인합니다.pattern
: 문자열이 정규식과 일치하는지 확인합니다.alphabetical
: 배열 요소 또는 객체 키가 알파벳 순서인지 확인합니다.length
: 문자열 또는 배열의 길이를 확인합니다.schema
: JSON Schema에 대해 값의 유효성을 검사합니다.
이러한 함수만으로는 충분하지 않고 자체 사용자 지정 함수를 작성해야 할 때 진정한 힘이 발휘됩니다. 바로 여기서 TypeScript가 빛을 발합니다.
TypeScript로 첫 번째 사용자 지정 Spectral 함수 만들기
간단한 사용자 지정 함수를 만들어 보겠습니다. 모든 API 작업 요약이 Title Case여야 하고 70자를 초과하지 않아야 한다는 규칙을 적용한다고 상상해 봅시다.
1단계: TypeScript에서 사용자 지정 함수 정의하기
예를 들어 src/customFunctions.ts
파일을 생성합니다:
TypeScript
import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';
interface TitleCaseLengthOptions {
maxLength: number;
}
// Custom function to check if a string is title-cased and within a max length
export const titleCaseAndLength: IFunction<string, TitleCaseLengthOptions> = (
targetVal,
options,
context
): IFunctionResult[] | void => {
const results: IFunctionResult[] = [];
if (typeof targetVal !== 'string') {
// Should not happen if 'given' path points to a string, but good practice
return [{ message: `Value at path '${context.path.join('.')}' must be a string.` }];
}
// Check for Title Case (simple check: first letter of each word is uppercase)
const words = targetVal.split(' ');
const isTitleCase = words.every(word => word.length === 0 || (word[0] === word[0].toUpperCase() && (word.length === 1 || word.substring(1) === word.substring(1).toLowerCase())));
if (!isTitleCase) {
results.push({
message: `Summary "${targetVal}" at path '${context.path.join('.')}' must be in Title Case.`,
path: [...context.path], // Path to the violating element
});
}
// Check for length
const maxLength = options?.maxLength || 70; // Default to 70 if not provided
if (targetVal.length > maxLength) {
results.push({
message: `Summary "${targetVal}" at path '${context.path.join('.')}' exceeds maximum length of ${maxLength} characters. Current length: ${targetVal.length}.`,
path: [...context.path],
});
}
return results;
};
설명:
- 타입 안전성을 위해
@stoplight/spectral-core
에서IFunction
및IFunctionResult
를 가져옵니다.IFunction<T = unknown, O = unknown>
는 두 개의 제네릭 인수를 받습니다:targetVal
(린트되는 값)의 타입을 위한T
와 함수에 전달된options
의 타입을 위한O
입니다. targetVal
:given
JSONPath가 가리키는 API 문서의 실제 값입니다.string
으로 타입을 지정했습니다.options
: 규칙 정의에서 전달된 옵션(예:{ "maxLength": 70 }
)을 포함하는 객체입니다. 이러한 옵션을 위해TitleCaseLengthOptions
인터페이스를 생성했습니다.context
: 다음을 포함하여 린팅 프로세스에 대한 컨텍스트 정보를 제공합니다:path
:targetVal
로 이어지는 경로 세그먼트의 배열입니다.document
: 전체 파싱된 문서입니다.rule
: 현재 처리 중인 규칙입니다.- 함수는 위반 사항이 있는 경우
IFunctionResult
객체 배열을 반환하고, 문제가 없는 경우void
/undefined
/빈 배열을 반환합니다. 각IFunctionResult
에는message
와 선택적으로path
(context.path
와 다른 경우)가 있어야 합니다. - 우리의 로직은 간단한 Title Case 형식과 최대 길이를 확인합니다.
2단계: TypeScript 함수 컴파일하기
.spectral.yaml
또는 .spectral.js
규칙 세트와 함께 이 함수를 사용할 계획이라면, functionsDir
를 가리키는 경우 TypeScript를 JavaScript로 컴파일해야 합니다.
package.json
에 빌드 스크립트를 추가합니다:
JSON
{
"scripts": {
"build": "tsc"
}
}
npm run build
또는 yarn build
를 실행합니다. 이렇게 하면 src/customFunctions.ts
가 dist/customFunctions.js
로 컴파일됩니다 (tsconfig.json
기준).
3단계: 규칙 세트 파일 생성하기
예를 들어 .spectral.js
규칙 세트를 만들어 보겠습니다 (완전히 TypeScript 기반 설정을 선호하는 경우 .spectral.ts
도 가능합니다. 다음 섹션을 참조하세요).
컴파일된 함수를 직접 참조하는 JavaScript 규칙 세트 파일을 사용하는 경우:
JavaScript
// .spectral.js
const { titleCaseAndLength } = require('./dist/customFunctions'); // Path to your compiled JS functions
module.exports = {
extends: [['@stoplight/spectral-rulesets/dist/rulesets/oas', 'recommended']],
rules: {
'operation-summary-title-case-length': {
description: 'Operation summaries must be title-cased and not exceed 70 characters.',
message: '{{error}}', // The message will come from the custom function
given: '$.paths[*][*].summary', // Targets all operation summaries
severity: 'warn',
formats: ["oas3"], // Apply only to OpenAPI v3 documents
then: {
function: titleCaseAndLength, // Directly reference the imported function
functionOptions: {
maxLength: 70,
},
},
},
// You can add more rules here
},
};
또는 .spectral.yaml
및 functionsDir
를 사용하는 경우:
먼저 dist
디렉토리에 index.js
가 포함되어 있는지 확인하거나 customFunctions.js
가 직접 내보내는지 확인하세요. 예를 들어, dist/customFunctions.js
에 exports.titleCaseAndLength = ...;
가 있다면 다음과 같이 할 수 있습니다:
YAML
# .spectral.yaml
extends:
- ["@stoplight/spectral-rulesets/dist/rulesets/oas", "recommended"]
functionsDir: "./dist" # Directory containing compiled custom JS functions
rules:
operation-summary-title-case-length:
description: "Operation summaries must be title-cased and not exceed 70 characters."
message: "{{error}}"
given: "$.paths[*][*].summary"
severity: "warn"
formats: ["oas3"]
then:
function: customFunctions#titleCaseAndLength # Assuming functions are exported from customFunctions.js
functionOptions:
maxLength: 70
여기서 customFunctions#titleCaseAndLength
는 Spectral에게 functionsDir
에서 customFunctions.js
(또는 customFunctions/index.js
)를 찾아 내보낸 titleCaseAndLength
함수를 사용하도록 지시합니다.
4단계: 샘플 OpenAPI 문서 생성하기
규칙을 테스트하기 위해 간단한 openapi.yaml
파일을 만들어 보겠습니다:
YAML
# openapi.yaml
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/items:
get:
summary: retrieves all items from the store # Incorrect: not title case
responses:
'200':
description: A list of items.
post:
summary: Adds A New Item To The Ever Expanding Collection Of Items In The Store # Incorrect: too long
responses:
'201':
description: Item created.
/users:
get:
summary: Get User Details # Correct
responses:
'200':
description: User details.
5단계: Spectral 실행하기
이제 OpenAPI 문서에 대해 Spectral CLI를 실행합니다:
Bash
spectral lint openapi.yaml --ruleset .spectral.js
# or if using YAML ruleset (often auto-detected if named .spectral.yaml)
spectral lint openapi.yaml
예상 출력:
다음과 유사한 경고가 표시될 것입니다:
openapi.yaml
2:10 warning operation-summary-title-case-length Summary "retrieves all items from the store" at path 'paths./items.get.summary' must be in Title Case. paths./items.get.summary
6:10 warning operation-summary-title-case-length Summary "Adds A New Item To The Ever Expanding Collection Of Items In The Store" at path 'paths./items.post.summary' exceeds maximum length of 70 characters. Current length: 78. paths./items.post.summary
✖ 2 problems (0 errors, 2 warnings, 0 infos, 0 hints)
이 출력은 JavaScript로 컴파일된 사용자 지정 TypeScript 함수가 위반 사항을 올바르게 식별하고 있음을 확인합니다.
TypeScript 규칙 세트를 사용한 Spectral 프로그래밍 방식 사용
더 복잡한 시나리오나 애플리케이션에 더 긴밀하게 통합하기 위해 Spectral을 프로그래밍 방식으로 사용하고 규칙 세트를 완전히 TypeScript로 정의할 수 있습니다. 이렇게 하면 원하는 경우 별도의 .spectral.yaml
또는 .spectral.js
파일이 필요 없으며 동적 규칙 세트 구성을 허용합니다.
예를 들어 린터용 TypeScript 파일 src/linter.ts
를 생성합니다:
TypeScript
import { Spectral, Document } from '@stoplight/spectral-core';
import { oas } from '@stoplight/spectral-rulesets';
import { truthy } from '@stoplight/spectral-functions'; // Example built-in function
import { titleCaseAndLength } from './customFunctions'; // Your custom TS function
import type { ISpectralDiagnostic } from '@stoplight/spectral-core';
import * as fs from 'fs/promises';
import * as path from 'path';
// Define the Spectral instance
const spectral = new Spectral();
// Load built-in rulesets and functions
spectral.setRuleset({
extends: [[oas, 'recommended']], // Extends the recommended OpenAPI rules
rules: {
'operation-summary-title-case-length': {
description: 'Operation summaries must be title-cased and not exceed 70 characters.',
message: '{{error}}',
given: '$.paths[*][*].summary',
severity: 'warn',
formats: ["oas3"],
then: {
function: titleCaseAndLength, // Directly use the TypeScript function
functionOptions: {
maxLength: 70,
},
},
},
'info-contact-defined': { // Example using a built-in function
description: 'Info object should have a contact object.',
message: 'API contact information is missing.',
given: '$.info',
severity: 'warn',
formats: ["oas3"],
then: {
field: 'contact',
function: truthy, // Using a built-in function
},
},
},
});
// Function to lint a document
export async function lintDocument(filePath: string): Promise<ISpectralDiagnostic[]> {
try {
const absolutePath = path.resolve(filePath);
const fileContent = await fs.readFile(absolutePath, 'utf-8');
// Create a Spectral Document object
// The second argument (URI) is important for resolving relative $refs if any
const document = new Document(fileContent, undefined, absolutePath);
const results = await spectral.run(document);
return results;
} catch (error) {
console.error('Error linting document:', error);
return [];
}
}
// Example usage (e.g., in a script or another module)
async function main() {
const diagnostics = await lintDocument('openapi.yaml'); // Path to your API spec
if (diagnostics.length > 0) {
console.log('API Linting Issues Found:');
diagnostics.forEach(issue => {
console.log(
`- [${issue.severity === 0 ? 'Error' : issue.severity === 1 ? 'Warning' : issue.severity === 2 ? 'Info' : 'Hint'}] ${issue.code} (${issue.message}) at ${issue.path.join('.')}`
);
});
} else {
console.log('No API linting issues found. Great job!');
}
}
// If you want to run this file directly with ts-node
if (require.main === module) {
main().catch(console.error);
}
실행 방법:
Bash
npx ts-node src/linter.ts
이 접근 방식은 최대의 유연성을 제공합니다:
- 규칙 세트 정의를 위한 컴파일 단계 없음: 규칙 세트 자체는 TypeScript로 정의됩니다. 사용자 지정 함수는 여전히 가져와야 합니다 (
ts-node
를 사용하는 경우 TS로, 순수 Node를 실행하는 경우 컴파일된 JS로). - 동적 규칙: 런타임 조건에 따라 규칙 세트를 프로그래밍 방식으로 구성하거나 수정할 수 있습니다.
- 직접적인 타입 사용: 가져온 TypeScript 함수를 규칙 세트 정의 내에서 직접 사용하여 타입 안전성 및 IDE 통합을 강화합니다.
고급 사용자 지정 함수 기법
사용자 지정 함수 생성의 몇 가지 더 고급 측면을 살펴보겠습니다.
비동기 사용자 지정 함수:
사용자 지정 함수가 비동기 작업(예: 유효성 검사를 위해 외부 리소스 가져오기, 성능을 위해 주의해서 사용)을 수행해야 하는 경우 비동기 함수로 정의할 수 있습니다.
TypeScript
import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';
export const checkExternalResource: IFunction<string, { url: string }> =
async (targetVal, options, context): Promise<IFunctionResult[] | void> => {
try {
const response = await fetch(`${options.url}/${targetVal}`);
if (!response.ok) {
return [{ message: `Resource '${targetVal}' not found at ${options.url}. Status: ${response.status}` }];
}
} catch (error: any) {
return [{ message: `Error fetching resource '${targetVal}': ${error.message}` }];
}
};
Spectral은 비동기 함수를 올바르게 await
할 것입니다.
전체 문서 및 해석된 값 접근하기:
context.document 객체는 전체 파싱된 문서에 접근할 수 있게 해줍니다. 더 강력하게, context.document.resolved는 $ref
포인터를 다룰 때 필수적인 문서의 완전히 역참조된 버전을 제공합니다.
TypeScript
import type { IFunction, IFunctionResult } from '@stoplight/spectral-core';
import {isPlainObject} from '@stoplight/json';
// Example: Ensure a referenced schema has a specific property
export const referencedSchemaHasProperty: IFunction<{$ref: string}, { propertyName: string }> = (
targetVal, // This will be the object like { $ref: '#/components/schemas/MySchema' }
options,
context
): IFunctionResult[] | void => {
if (!targetVal.$ref) return;
// The `context.document.resolveAnchor` method can find the resolved value for a $ref
const resolvedValue = context.document.resolveAnchor(targetVal.$ref);
if (!resolvedValue || !isPlainObject(resolvedValue.value)) {
return [{ message: `Could not resolve $ref: ${targetVal.$ref}` }];
}
const schemaProperties = resolvedValue.value.properties as Record<string, unknown> | undefined;
if (!schemaProperties || schemaProperties[options.propertyName] === undefined) {
return [{
message: `The schema referenced by "${targetVal.$ref}" at path '${context.path.join('.')}' must have property "${options.propertyName}".`,
path: [...context.path, '$ref'] // Point to the $ref itself
}];
}
};
이 함수는 예를 들어 $.paths[*].*.responses.*.content.*.schema
와 같이 $ref
를 포함하는 객체를 대상으로 하는 given
경로와 함께 사용됩니다.
형식 작업하기:
사용자 지정 규칙이 적용될 형식(예: oas2, oas3, asyncapi2)을 지정하는지 확인하세요. 이렇게 하면 호환되지 않는 문서 타입에서 규칙이 실행되는 것을 방지할 수 있습니다. 함수 내에서 context.document.formats를 통해 감지된 형식에 접근할 수 있습니다.
규칙 세트 구성 및 확장하기
사용자 지정 규칙 모음이 늘어남에 따라 구성이 중요해집니다.
- 모듈화된 규칙 세트: 큰 규칙 세트를 더 작고 집중된 파일로 분할합니다.
extends
속성을 사용하여 결합할 수 있습니다.
JavaScript
// .spectral.js (main ruleset)
module.exports = {
extends: ['./rulesets/common-rules.js', './rulesets/security-rules.js'],
// ... other rules or overrides
};
- 공유 규칙 세트: 다른 프로젝트나 팀과 공유하기 위해 규칙 세트를 npm 패키지로 게시합니다. 그런 다음
extends
속성은 이러한 패키지를 참조할 수 있습니다. functionsDir
를 사용한 단순성: 사용자 지정 JS 함수(TS에서 컴파일됨)가 많은 경우,.spectral.yaml
규칙 세트의functionsDir
는 모든 함수를 나열하거나 완전히 프로그래밍 방식으로 설정하는 것보다 간단할 수 있습니다. 컴파일된 JS 파일이 함수를 올바르게 내보내는지 확인하기만 하면 됩니다.
워크플로우에 Spectral 통합하기
API 린팅의 진정한 힘은 자동화될 때 발휘됩니다.
- CI/CD 파이프라인: GitHub Actions, GitLab CI, Jenkins 또는 기타 CI/CD 파이프라인에
spectral lint
명령을 통합합니다. 심각한 오류가 감지되면 빌드를 실패시킵니다.
YAML
# Example GitHub Action step
- name: Lint API Specification
run: spectral lint ./path/to/your/api-spec.yaml --ruleset ./.spectral.js --fail-severity=error
- Git Hooks: Husky와 같은 도구를 사용하여 pre-commit 또는 pre-push 훅에서 Spectral을 실행하여 저장소에 도달하기 전에 문제를 포착합니다.
- IDE 통합: IDE(예: VS Code)용 Spectral 확장 프로그램을 찾아 API 사양을 작성할 때 실시간 피드백을 받으세요.
Spectral 및 TypeScript 사용자 지정 규칙 모범 사례
- 명확한 설명 및 메시지: 규칙에 대해 의미 있는
description
및message
속성을 작성합니다. 메시지는 사용자가 문제를 해결하는 방법을 안내해야 합니다. - 적절한 심각도 수준: 심각한 위반에는
error
, 중요한 제안에는warn
, 정보 제공용 검사에는info
, 사소한 제안에는hint
를 사용합니다. - 정확한
given
경로: JSONPath 표현식을 가능한 한 구체적으로 만들어 의도한 노드만 대상으로 합니다. 이는 성능을 향상시키고 오탐을 줄입니다. - 멱등 함수 (Idempotent Functions): 사용자 지정 함수는 가능한 한 순수해야 합니다. 즉, 동일한 입력이 주어지면 부작용 없이 동일한 출력을 생성해야 합니다.
- 사용자 지정 규칙 테스트하기: 사용자 지정 TypeScript 함수에 대한 단위 테스트를 작성하여 다양한 입력에 대해 예상대로 작동하는지 확인합니다.
- 성능 고려 사항: 매우 큰 문서의 경우 성능에 민감한 규칙에서 복잡한 계산이나 수많은
schema
함수 호출에 유의하십시오. 규칙 세트의 성능을 테스트하십시오. - TypeScript 타입 최신 상태 유지하기: 사용자 지정 함수가 예상하는
targetVal
및options
의 타입을 올바르게 지정하는지 확인합니다. 이는 유지보수성에 매우 중요합니다.
결론: Spectral 및 TypeScript로 API 거버넌스 수준 높이기
Spectral은 API 린팅을 위한 강력한 프레임워크를 제공하며, 사용자 지정 규칙 개발에 TypeScript를 활용함으로써 API 거버넌스 전략에 향상된 타입 안전성, 개발자 경험 및 유지보수성을 가져옵니다. OpenAPI 모범 사례, 회사별 명명 규칙 또는 API 설계 내의 복잡한 비즈니스 로직을 적용하든 관계없이, Spectral의 유연한 규칙 엔진과 TypeScript의 강력한 타이핑 시스템의 조합은 강력한 솔루션을 제공합니다.
IDE 피드백부터 CI/CD 파이프라인까지 개발 라이프사이클에 Spectral을 통합함으로써 API가 일관되고 고품질이며 정의된 표준을 준수하도록 보장할 수 있으며, 궁극적으로 더 안정적이고 사용하기 쉬운 API로 이어집니다. 작게 시작하고, 규칙 세트를 반복하며, 팀에게 더 나은 API를 구축하도록 권한을 부여하십시오.