Prettier 정복 1

i33W·2024년 8월 13일

Prettier

목록 보기
1/1

Prettier는 무엇인가?


요약

  • An opinionated code formatter 독선적인 코드 포맷터
  • Supports many languages 다양한 언어를 지원하는
  • Integrates with most editors 대부분의 에디터와 호환되는
  • Has few options 적은 옵션을 가진

예시

아래와 같은 코드가 있다고 가정하자.

foo(arg1, arg2, arg3, arg4);

지금은 괜찮은데 만약 아래와 같이 인수의 이름이 길어지는 상황이 생긴다면?

foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());

코드가 너무 길어진다. 이런 경우 Prettier는 원래 스타일을 무시하고 구문 분석하여 최대 줄 길이를 고려한 자체 규칙에 따라 구문 분석된 AST를 다시 인쇄하고 필요한 경우 코드를 래핑하여 전체 코드베이스에서 일관된 코드 스타일 (즉, AST에 영향을 미치지 않는 코드 형식)을 적용한다.

foo(
  reallyLongArg(),
  omgSoManyParameters(),
  IShouldRefactorThis(),
  isThereSeriouslyAnotherOne(),
);

왜 Prettier를 써야 할까?


요약

  • Your code is formatted on save 코드 저장할 때 포맷팅 됨
  • No need to discuss style in code review 코드리뷰할 때 코드 스타일에 대해서 왈가왈부 안해도 됨
  • Saves you time and energy 시간과 에너지를 아끼자
  • And more 기타 등등

1. 강제적으로 스타일 가이드 구축 및 적용이 가능함!

Prettier를 채택한 가장 큰 이유는 스타일과 관련된 모든 지속적인 논쟁을 중단하기 위함
일반적으로 공통된 스타일 가이드를 갖는 것이 프로젝트와 팀에 가치 있다는 것은 받아들여지지 만, 거기에 도달하는 것은 매우 고통스럽고 보람 없는 과정임
사람들은 특정 코드 작성 방식에 대해 매우 감정적이며, 아무도 시간을 들여서 코드를 작성하고 엉터리 메시지를 받는 것을 좋아하지 않음

2. 초보자/새로운 팀원에게 도움이 됨!

초보자들에게 유용함(구문 실수 감소)
회사에 합류한 숙련된 엔지니어가 이전에 다른 코딩 스타일을 사용했을 가능성이 높고, 개발자가 다른 프로그래밍 언어에서 왔기 때문에 램프업 시간이 빨라짐

3. 코드 작성에 용이함!

Prettier를 사용하지 않으면 많은 시간과 정신적 에너지를 코드 포맷에 사용하게 됨
Prettier 편집기 통합을 사용하면 마법의 키 바인딩을 누르기만 하면 휙, 코드가 포맷됨

4. 도입이 쉬움!

Prettier는 가장 논란이 적은 코딩 스타일을 사용하기 위해 매우 열심히 노력했고, 모든 엣지 케이스를 수정하는 여러 차례를 거쳤으며, 시작 경험을 다듬었음
Prettier를 코드베이스에 푸시할 준비가 되면 기술적으로 고통스럽지 않을 뿐만 아니라 새로 포맷된 코드베이스는 큰 논란을 일으키지 않고 동료들에게 고통 없이 받아들여질 것임

5. 기존 코드베이스를 정리할 수 있음!

코딩 스타일을 생각해 내고 이를 적용하는 것은 큰 과제이기 때문에 종종 간과되고 일관되지 않은 코드베이스에서 작업하게 됨
이 경우 Prettier를 실행하면 코드베이스가 균일해지고 거의 시간을 들이지 않고도 읽기가 더 쉬워짐

6. 많은 사람들이 쓰고 있음!

React 및 React Native와 동일한 사람들이 만들었음
모든 주요 JS 프로젝트에서 이미 채택됨

Prettier vs. Linters


ESLint/TSLint/stylelint 등등과 무슨 차이인가?

Linter에는 두 가지 유형의 규칙이 있음

  1. 서식 규칙 : 예: 최대 길이 , 공백과 탭 혼합 금지 , 키워드 간격 , 쉼표 스타일 …

Prettier는 이 모든 종류의 규칙에 대한 필요성을 완화합니다! Prettier는 일관된 방식으로 전체 프로그램을 처음부터 다시 인쇄하므로 프로그래머가 더 이상 실수를 할 수 없습니다 :)

  1. 코드 품질 규칙 : 예: no-unused-vars , no-extra-bind , no-implicit-globals , prefer-promise-reject-errors …

Prettier는 그런 종류의 규칙을 돕는 데 아무런 도움이 되지 않습니다. 또한 이 규칙은 linter가 제공하는 가장 중요한 규칙이기도 합니다. 코드에서 실제 버그를 잡을 가능성이 높기 때문입니다!

즉, Prettier를 사용해 포맷을 지정 하고 linter를 사용해 버그를 잡으세요!

Prettier가 신경쓰는 것


유효한 코드가 생성되는가?

형식 지정 전과 정확히 동일한 동작을 갖는 유효한 코드를 출력하는 것
Prettier가 이러한 정확성 규칙을 따르지 않는 코드가 있다면 보고할 것
(이는 수정이 필요한 버그입니다!)

문자열에 대해 - 큰따옴표 또는 작은따옴표?

Prettier는 이스케이프가 가장 적은 것을 선택함

  • O: "It's gettin' better!"
  • X: "It\'s gettin' better!"

동점이거나 문자열에 따옴표가 없는 경우 Prettier는 기본적으로 큰따옴표를 사용(하지만 singleQuote 옵션을 통해 변경할 수 있음)

JSX에는 따옴표에 대한 자체 옵션이 있음: jsxSingleQuote
JSX는 HTML에서 유래했으며, 속성에 대한 따옴표의 주요 사용은 큰따옴표입니다. 브라우저 개발자 도구도 이 규칙을 따르며 소스 코드에 작은따옴표가 사용되더라도 항상 HTML을 큰따옴표로 표시합니다.
(별도의 옵션을 사용하면 JS에 작은따옴표를 사용하고 "HTML"(JSX)에 큰따옴표를 사용할 수 있음)

Prettier는 문자열이 이스케이프되는 방식을 유지합니다. 예를 들어 "🙂""\uD83D\uDE42"로 포맷되지 않으며 그 반대의 경우도 마찬가지입니다.

Empty lines 빈 줄

빈 줄은 자동으로 생성하기 매우 어려움, Prettier는 빈 줄을 원래 소스 코드에 있던 방식대로 보존함. 다만, 두 가지 추가 규칙이 있음

  • Prettier는 여러 빈 줄을 하나의 빈 줄로 축소함
  • 블록(및 전체 파일)의 시작과 끝에 있는 빈 줄은 제거됨(하지만 파일은 항상 하나의 줄바꿈으로 끝납니다.)

Multi-line objects 다중 줄 객체

기본적으로 Prettier의 알고리즘은 표현식이 맞으면 한 줄에 표현식을 표시함
하지만 JavaScript에서 객체는 다양한 용도로 사용되며, 때로는 여러 줄로 유지하면 가독성이 좋아짐. (예: 객체 목록, 중첩 구성, 스타일시트, 키 지정 메서드)
모든 경우에 대한 좋은 규칙을 찾을 수 없었기 때문에 Prettier는 대신 원래 소스 코드에서 {와 첫 번째 키 사이에 줄 바꿈이 있는 경우 객체를 여러 줄로 유지함.

그 결과 긴 단일 줄 객체는 자동으로 확장되지만 짧은 다중 줄 객체는 절대 축소되지 않음
이렇게 일관성이 없어서 Prettier는 이 속성을 없애고 싶어함

팁1: 단일 줄로 합치려는 다중 줄 객체가 있는 경우:

const user = {
  name: "John Doe",
  age: 30,
};

...당신이 할 일은 { 다음에 오는 새로운 줄을 아래와 같이 지우는 것임:

const user = {  name: "John Doe",
  age: 30
};

...그럼 Prettier가 알아서 아래와 같이 포맷팅해줌

const user = { name: "John Doe", age: 30 };

팁2: 팁1과 반대로 다중 줄로 만들려는 단일 줄 객체가 있는 경우:
...당신이 할 일은 { 다음에 새로운 줄을 아래와 같이 넣는 것임:

const user = {
 name: "John Doe", age: 30 };

...그럼 Prettier가 알아서 아래와 같이 포맷팅해줌

const user = {
  name: "John Doe",
  age: 30,
};

Decorators

객체와 마찬가지로 데코레이터는 다양한 용도로 사용됨. 때로는 데코레이터를 데코레이션하는 줄 위에 쓰는 것이 합리적이고, 때로는 같은 줄에 쓰는 것이 더 좋음. 이에 대한 좋은 규칙을 찾지 못했기 때문에 Prettier는 데코레이터를 작성한 대로 배치함(줄에 맞는 경우). 이상적이지는 않지만 어려운 문제에 대한 실용적인 해결책.

@Component({
  selector: "hero-button",
  template: `<button>{{ label }}</button>`,
})
class HeroButtonComponent {
  // These decorators were written inline and fit on the line so they stay
  // inline.
  @Output() change = new EventEmitter();
  @Input() label: string;

  // These were written multiline, so they stay multiline.
  @readonly
  @nonenumerable
  NODE_TYPE: 2;
}

다만, 클래스는 예외임. 클래스는 항상 자체 라인으로 이동함(Prettier 1.14.x 버전은 확인 필요)

// Before running Prettier:
@observer class OrderLine {
  @observable price: number = 0;
}
// After running Prettier:
@observer
class OrderLine {
  @observable price: number = 0;
}

마지막으로: TC39는 아직 데코레이터가 내보내기 전에 오는지, 아니면 내보내기 후에 오는지 결정하지 않았음, 그동안 Prettier는 둘 다 지원함

@decorator export class Foo {}
export @decorator class Foo {}

Template literals

템플릿 리터럴은 보간을 포함할 수 있음. 보간 내에 줄 바꿈을 삽입하는 것이 적절한지 여부를 결정하는 것은 불행히도 템플릿의 의미적 내용에 따라 달라짐. 예를 들어, 자연어 문장 중간에 줄 바꿈을 도입하는 것은 일반적으로 바람직하지 않음. Prettier는 이 결정을 내릴 만큼 충분한 정보가 없기 때문에 객체에 사용되는 것과 유사한 휴리스틱을 사용함. 보간 내에 이미 줄 바꿈이 있는 경우에만 보간 표현식을 여러 줄로 나눔.

즉, 다음과 같은 리터럴은 인쇄 너비를 초과하더라도 여러 줄로 나뉘지 않습니다.

이것은 보간을 포함하는 긴 메시지입니다: ${format(data)} <- 이것처럼;

Prettier에서 보간을 분할하려면 ${...} 내에 줄 바꿈이 있는지 확인해야 함. 그렇지 않으면 아무리 길어도 모든 것을 한 줄로 유지

Prettier에서는 이런 방식으로 원래 서식에 의존하고 싶지 않지만, 현재로선 이것이 우리가 할 수 있는 최선의 방법임.

noSemi 옵션을 사용하는 방법

다음 코드를 고려해 보세요.

if (shouldAddLines) {
	[-1, 1].forEach(delta => addLine(delta * 20))
}

위의 코드는 세미콜론 없이도 잘 작동하지만, Prettier는 실제로 이를 다음과 같이 바꿈

if (shouldAddLines) {
	;[-1, 1].forEach(delta => addLine(delta * 20))
}

이것은 실수를 피하는 데 도움이 됨. Prettier가 세미콜론을 삽입하지 않고 이 줄을 추가한다고 상상해 보세요.

if (shouldAddLines) {
+	console.log('Do we even get here??')
	[-1, 1].forEach(delta => addLine(delta * 20))
}

위의 내용은 실제로 다음을 의미함

if (shouldAddLines) {
	console.log('Do we even get here??')[-1, 1].forEach(delta => addLine(delta * 20))
}

[ 앞에 세미콜론이 있으면 이런 문제는 발생하지 않음
줄을 다른 줄과 독립적으로 만들어 ASI 규칙을 생각하지 않고도 줄을 이동하고 추가할 수 있음

이 관행은 세미콜론 없는 스타일을 사용하는 표준에서도 일반적임
Prettier는 코드만 다시 포맷할 뿐 코드의 동작을 변경하지 않는다는 점을 기억할 것(:

console.log('Running a background task')
(async () => {
	await doBackgroundWork()
})()

이것을 Prettier에 입력하면 이 코드의 동작은 변경되지 않고, 대신 이 코드가 실행될 때 실제로 어떻게 동작하는지 보여주는 방식으로 다시 포맷됨

console.log("Running a background task")(async () => {
await doBackgroundWork();
})();

printWidth 옵션은 Prettier에 대한 엄격한 규칙이라기보다는 가이드라인에 가까움. 허용되는 최대 줄 길이 제한이 아님. Prettier에 줄 길이를 대략 얼마로 할 것인지 알려주는 방법. Prettier는 더 짧거나 긴 줄을 만들지만 일반적으로 지정된 인쇄 너비를 충족하려고 노력함.

정말 긴 문자열 리터럴, 정규 표현식, 주석 및 변수 이름과 같이 줄을 나눌 수 없는 몇 가지 예외 사례가 있음(Prettier가 하지 않는 코드 변환을 사용하지 않고). 또는 코드를 50단계 깊이로 중첩하면 줄은 물론 대부분 들여쓰기가 됨 :)

그 외에도 Prettier가 의도적으로 인쇄 너비를 초과하는 경우가 2 가지 있음

1. Imports, require
Prettier는 긴 import 문을 여러 줄로 나눌 수 있음

import {
CollectionDashboard,
DashboardPlaceholder,
} from "../components/collections/collection-dashboard/main";

다음 예는 인쇄 너비에 맞지 않지만 Prettier는 어쨌든 한 줄로 표시함

import { CollectionDashboard } from "../components/collections/collection-dashboard/main";

일부에게는 예상치 못한 일일 수 있지만, 단일 요소가 있는 import를 한 줄로 유지하는 것이 일반적인 요청이었기 때문에 이렇게 함. require 호출에도 동일하게 적용.

2. Testing functions
또 다른 일반적인 요청은 너무 길어지더라도 긴 테스트 설명은 한 줄로 유지해 달라는 것이었음. 이런 경우 인수를 새 줄로 래핑해도 별 도움이 안됨.

describe("NodeRegistry", () => {
it("캐시가 오래되었더라도 미리 페치할 노드가 없으면 요청하지 않습니다", async () => {
// 위의 줄은 인쇄 너비를 초과하지만 어쨌든 한 줄로 유지되었습니다.
});
});

Prettier에는 describe, it 및 test와 같은 일반적인 테스트 프레임워크 함수에 대한 특수 사례가 있음

JSX

Prettier는 JSX가 관련될 때 다른 JS와 약간 다르게 출력합니다.

function greet(user) {
  return user
    ? `Welcome back, ${user.name}!`
    : "Greetings, traveler! Sign up today!";
}

function Greet({ user }) {
  return (
    <div>
      {user ? (
      	<p>Welcome back, {user.name}!</p>
      ) : (
      	<p>Greetings, traveler! Sign up today!</p>
      )}
    </div>
  );
}

2 가지 이유가 있음

  1. 많은 사람들이 이미 JSX를 괄호로 묶었음(특히 return 문에서) Prettier는 이 일반적인 스타일을 따름
  2. 대체 서식 덕분에 JSX를 편집하기가 더 쉬움. 세미콜론을 남기기 쉬움. 일반 JS와 달리 JSX의 남은 세미콜론은 페이지에 일반 텍스트로 표시될 수 있음.
<div>
	<p>안녕하세요, 여행자 여러분! 오늘 가입하세요!</p>; {/* <-- 어머나! */}
</div>

주석

주석은 그대로 둠(무엇이든 포함할 수 있기 때문). 이에 대한 유일한 예외는 JSDoc 스타일 주석(모든 줄이 *로 시작하는 블록 주석)으로, Prettier가 들여쓰기를 수정할 수 있음

그런 다음 주석을 어디에 둘 것인가는 정말 어려운 문제임. Prettier는 주석을 대략 원래 위치에 두려고 최선을 다하지만 주석은 거의 어디에나 둘 수 있기 때문에 쉬운 일이 아님

일반적으로 줄 끝에 주석을 두는 대신 자체 줄에 주석을 두면 가장 좋은 결과를 얻을 수 있음

  • 권장O: // eslint-disable-next-line
  • 권장X: // eslint-disable-line

eslint-disable-next-line$FlowFixMe와 같은 "매직 주석"은 Prettier가 표현식을 여러 줄로 나누기 때문에 수동으로 이동해야 할 때가 있음

이 코드를 상상해보세요!

// eslint-disable-next-line no-eval
const result = safeToEval ? eval(input) : fallback(input);

그런 다음 다른 조건을 추가해야 함

// eslint-disable-next-line no-eval
const result = safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

Prettier는 위의 내용을 다음과 같이 바꿈

// eslint-disable-next-line no-eval
const result =
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

즉, eslint-disable-next-line 주석이 더 이상 유효하지 않음. 이 경우 주석을 이동해야 함

const result =
// eslint-disable-next-line no-eval
safeToEval && settings.allowNativeEval ? eval(input) : fallback(input);

가능하다면 줄 범위(예: eslint-disable 및 eslint-enable) 또는 명령문 수준(예: / istanbul ignore next /)에서 작동하는 주석을 사용할 것(더욱 안전) eslint-plugin-eslint-comments를 사용하여 eslint-disable-line 및 eslint-disable-next-line 주석 사용을 허용하지 않을 수 있음

Prettier가 무시하는 것


Prettier는 코드를 인쇄만 합니다. 변환은 하지 않습니다. 이는 Prettier의 범위를 제한하기 위한 것입니다. 인쇄에 집중해서 정말 잘 해보겠습니다!

Prettier의 범위를 벗어나는 몇 가지 예를 들어보겠습니다.

  • 작은 따옴표나 큰 따옴표로 묶은 문자열을 템플릿 리터럴로 바꾸거나 그 반대로 바꾸기.
  • +를 사용하여 긴 문자열 리터럴을 인쇄 너비에 맞는 부분으로 나누기.
  • {}와 return을 선택 사항인 곳에 추가/제거하기.
  • ?:를 if-else 문으로 바꾸기.
  • 가져오기, 객체 키, 클래스 멤버, JSX 키, CSS 속성 또는 기타 모든 것을 정렬/이동하기.

위에서 언급했듯이 단순히 인쇄하는 것이 아니라 변환하는 것 외에도 정렬은 부작용(예: 가져오기의 경우)으로 인해 잠재적으로 안전하지 않으며 가장 중요한 정확성 목표를 확인하기 어렵게 만듦

profile
더 오래하면 돼.

0개의 댓글