본문 바로가기

개발/React

eslint-lint-sprinkles-rule 제작기 - (1) 문제인지

반응형

 

https://www.svgrepo.com/svg/374153/vanilla-extract

 

목차

 

  1. eslint-lint-sprinkles-rule 제작기 - (1) 문제인지
  2. eslint-lint-sprinkles-rule 제작기 - (2) sprinkles-rule 작성을 위한 배경 지식
  3. eslint-lint-sprinkles-rule 제작기 - (3) sprinkles-rule 내부를 구성하는 코드 설명

 

글이 너무 길어지는 것을 방지하여 문제인지와 해결과정에 대한 글을 나누어 작성하려고 한다.

 

 

 

서론

 

최근에 디렉토리 구조에 대한 공부를 진행하던 중 FSD(Feature-Sliced-Directory)에 대한 글을 읽고 많은 생각을 하게 되었다. 그러던 와중에 링크드인에 훌륭한 개발자분이 FSD 에 대한 Lint를 제작하신 것을 흥미롭게 보았고, 그동안 왜 우리 팀에 맞는 LInt Rule을 만들 생각을 안했는지 후회가 되어 바로 팀에 있는 문제들이 무엇이 있었는지 고민해보았다.

 

(여담이지만 위의 FSD Lint에 대한 글은 이곳에 있고 한번쯤 읽어보는 것을 추천한다. https://fantasmith.com/challenge/opensource/fsd_lint/)

 

그러던 와중 최근 Next.js Page Router에서 App Router로의 전환을 수행하면서 style 관리를 style-components에서 vanilla-extract로 migration 하게되었던 것이 생각났다.

 

 

 

문제인지

 

vanilla-extract는 className에 주입할 style을 사전에 css.ts 파일에 만들어두고 이를 import 해서 사용하는데, 이렇게 사용하면 어쩔 수 없이 className과 실제 빌드 결과물에 나타나는 css 코드의 길이가 비례해서 늘어난다. 

 

이러한 문제를 해결하기 위해 utility로 제공하는 것이 바로 sprinkles 이다.

 

sprinkles란 단어 그대로 직역하면 컵케이크에 올라가는 알록달록한 설탕조각같은 것을 가리키는데, 이것이 결국 syntax sugar와 같은 역할을 함을 다른 단어로 표현한 것이다.

 

실제로 sprinkles를 사용하면 프로젝트 내에서 자주 사용하는 property와 value를 미리 정해둘 수 있고, 혹은 자주 사용하는 property는 shorthands를 통해 축약어로 사용할 수 있게된다. 이렇게 하면 sprinkles에 작성해둔 property와 value의 조합들로 미리 style sheet가 하나 생성되고, 이후 sprinkles를 사용하는 경우에는 해당 조합이 존재하는 style sheet 에서 class name을 가지고와서 이어붙이게 되는 구조이다.

 

 

// sprinkles.css.ts

// sprinkles는 아래와 같이 미리 선언해둔다.
const preDefinedProperties = defineProperties({
  properties: {
    marginTop: [1, 2, 3, 4, 5, 'auto'],
    marginBottom: [1, 2, 3, 4, 5, 'auto'],
    marginLeft: [1, 2, 3, 4, 5, 'auto'],
    marginRight: [1, 2, 3, 4, 5, 'auto'],
  },
  shorthands: {
    mt: ['marginTop'],
    mb: ['marginBottom'],
    ml: ['marginLeft'],
    mr: ['marginRight'],
  })
  
export const sprinkles = createSprinkles(preDefinedProperties);
  
  


// 위와 같이 작성하면 실제로는 아래와 같이 Style Sheet가 만들어진다.

/* 각 속성별로 클래스가 생성됨 */

/* marginTop */
.marginTop_1 { margin-top: 1px; }
.marginTop_2 { margin-top: 2px; }
.marginTop_3 { margin-top: 3px; }
.marginTop_4 { margin-top: 4px; }
.marginTop_5 { margin-top: 5px; }
.marginTop_auto { margin-top: auto; }

/* marginBottom */
.marginBottom_1 { margin-bottom: 1px; }
.marginBottom_2 { margin-bottom: 2px; }
.marginBottom_3 { margin-bottom: 3px; }
.marginBottom_4 { margin-bottom: 4px; }
.marginBottom_5 { margin-bottom: 5px; }
.marginBottom_auto { margin-bottom: auto; }

/* marginLeft */
.marginLeft_1 { margin-left: 1px; }
.marginLeft_2 { margin-left: 2px; }
.marginLeft_3 { margin-left: 3px; }
.marginLeft_4 { margin-left: 4px; }
.marginLeft_5 { margin-left: 5px; }
.marginLeft_auto { margin-left: auto; }

/* marginRight */
.marginRight_1 { margin-right: 1px; }
.marginRight_2 { margin-right: 2px; }
.marginRight_3 { margin-right: 3px; }
.marginRight_4 { margin-right: 4px; }
.marginRight_5 { margin-right: 5px; }
.marginRight_auto { margin-right: auto; }

 

 

 

이렇게 하면 sprinkles에 미리 정의해둔 값을 사용할 때에는 모든 Style에 대해 새로 생성되는 것은 아니고 일부는 선언된 것을 사용하고 없는 것만 새로 생성하게 되어 Line이 비례해서 증가하는 것을 줄일 수 있게 되는 것이다.

 

 

// 아래와 같이 선언하면 실제로는 test에 대해서는 display: 'flex' 를 가진 클래스만 생성된다.

const test = style([
  sprinkles({
    mr: 3
  }),
  {
    display: 'flex'
  }
])


// 실제 결과물 예시

/* 자동 생성된 sprinkles 스타일 */
.marginRight_3_hash123 {
  margin-right: 3px;
}

/* style() 함수로 생성된 스타일 */
.style_hash456 {
  display: flex;
}

 

 

하지만 이런 방식은 단점은 모든 스타일을 사전에 작성해둘 수는 없기때문에 자주 사용하는 것만 sprinkles에 작성해두고, 작성하는 사람이 sprinkles에 어떤게 선언되어있는지 정확하게 알고있어야 선언되어있는 것과 선언되어있지 않은 것을 구분해 사용할 수 있다는 점이다.

 

물론 선언되어있지 않은 것을 sprinkles에서 사용하면 에러가 발생하지만 선언되어있음에도 style에 선언하게된다면 의도치않게 Line이 늘어나게 되는 문제가 있었다.

 

따라서 ESLint를 통해 sprinkles에 미리 정의되어있는 값을 style에서 사용하는 경우에는 안내해주어 작성자가 바로 알 수 있도록 하는 것이 필요하다고 생각했기때문에 이 프로젝트를 진행해보기로 했다.

 

 

그렇다면 위와 같은 케이스는 sprinkles에 모두 선언해두면 되는게 아닐까 생각할 수 있다. 하지만 단발성으로 사용하는 스타일을 모두 sprinkles에 선언해두는 것은 해당 스타일을 사용하지 않는 곳에서의 접근 시에도 부득이하게 해당 스타일에 대한 코드가 Sprinkles Style Sheet에 포함되기때문에 이에 대해서는 sprinkles에 선언하지 않고 style로 사용하는 것이 더 효율적이라고 생각했다.

 

 

결론

 

평소에는 한 호흡으로 프로젝트에 대한 내용을 모두 정리했었는데, 그러다보니 담고싶은 내용이 많다보니 가독성은 떨어진다고 느끼게 되었다. 이번 프로젝트는 문제에 대한 인식단계와 이를 해결해나가는 과정을 분리해서 작성하는 방법을 선택해보려해서 실제 해결과정은 다음 글로 남겨보겠다.

 

 

결과물 : https://github.com/Sangminnn/eslint-plugin-sprinkles-lint

반응형