[{"data":1,"prerenderedAt":264},["ShallowReactive",2],{"$fMKV-1W5A9js3BpXjdHsVGr-P0zefHziYyWhS_ZF_QfM":3,"mdc--c5ye3t-key":19},{"post_id":4,"title":5,"description":6,"published_at":7,"tags":8,"content":17,"status":18},"1858ebfe-603c-41aa-8a5a-145eb7f2688d","ESlint는 formatter의 꿈을 꾸는가","Linter도 꿈을 꾸나?","2025-08-01",[9,12,14,16],{"tag":10,"category":11},"ESlint","dev",{"tag":13,"category":11},"Prettier",{"tag":15,"category":11},"Linter",{"tag":11,"category":11},"[Nuxt](https:\u002F\u002Fnuxt.com\u002F) 프레임워크는 전용 [ESlint](https:\u002F\u002Feslint.org\u002F) 모듈을 제공한다. [Nuxt ESlint](https:\u002F\u002Feslint.nuxt.com\u002F)라는 것인데, 이번에 나를 위한 CMS를 구축하려고 대시보드 웹 앱을 Nuxt 프레임워크 기반으로 만들게 되면서 CLI로 프로젝트를 생성할 때 함께 설치하게 되었다.\n\n[Prettier](https:\u002F\u002Fprettier.io\u002F)만 써오던 나에게 ESlint의 설정은 생소하고 어려웠다. 그러다 [antfu](https:\u002F\u002Fantfu.me\u002F)가 만든 [ESlint configuration 레포지토리](https:\u002F\u002Fgithub.com\u002Fantfu\u002Feslint-config)를 알게 되었다.\n\n(antfu는 [자신의 글](https:\u002F\u002Fantfu.me\u002Fposts\u002Fwhy-not-prettier)에서 Prettier를 사용하지 않고 ESlint로 포맷팅까지 하는 이유에 대해 밝힌 적이 있다.)\n\nNuxt ESlint가 제공하는 기본적인 설정에 antfu의 설정을 병합할 수 있다. 아래와 같은 코드로 설정이 가능하다.\n\n```typescript\n\u002F\u002F eslint.config.mjs\n\n\u002F\u002F @ts-check\nimport { antfu } from '@antfu\u002Feslint-config'\nimport withNuxt from '.\u002F.nuxt\u002Feslint.config.mjs'\n\nexport default withNuxt(\n  antfu({\n    vue: true,\n    typescript: true,\n    formatters: {\n      css: true,\n      html: true,\n      markdown: 'prettier',\n    },\n  }),\n)\n```\n\n이건 나의 설정 코드를 그대로 옮긴 것인데, 사실 `vue`, `typescript` 같은 필드는 필요하지 않다고 생각했다. 그런데 [WebStorm](https:\u002F\u002Fwww.jetbrains.com\u002Fko-kr\u002Fwebstorm\u002F)의 저장 액션으로 ESlint fix를 아무리 설정해도 코드 포맷팅이 되질 않았다. 그래서 추가했고, `formatter` 필드 내부의 `css`, `html`, `markdown`과 같은 필드들은 사용하지 않으면 해당 언어의 포맷팅이 전혀 되지 않으므로, 해당 언어로 코드를 쓴다면 무조건 추가해주는 게 좋다.\n\n이 과정에서 계속 ESlint의 컴파일 에러(?)가 나곤 했는데 위 설정과 더불어 꼭 해줘야 하는 설정이 있었는데 빼먹어서 그런 거였다. Nuxt의 전체적인 설정을 아래와 같이 해주어야 한다.\n\n```typescript\n\u002F\u002F nuxt.config.ts\nimport tailwindcss from '@tailwindcss\u002Fvite'\n\n\u002F\u002F https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fconfiguration\u002Fnuxt-config\nexport default defineNuxtConfig({\n  modules: [\n    '@nuxt\u002Ffonts',\n    '@nuxt\u002Ftest-utils',\n    '@pinia\u002Fnuxt',\n    'pinia-plugin-persistedstate\u002Fnuxt',\n    '@nuxt\u002Feslint',\n  ],\n  devtools: { enabled: true },\n  css: ['~\u002Fassets\u002Fcss\u002Fmain.css'],\n  compatibilityDate: '2025-05-15',\n  vite: { plugins: [tailwindcss()] },\n  eslint: {\n    config: {\n      standalone: false,\n    },\n  },\n})\n```\n\n이 코드는 내 프로젝트의 Nuxt configuration 코드를 그대로 옮긴 것이다. 이 코드의 마지막 부분에 eslint 필드로 추가된 `standalone` 필드값을 `false` 로 꼭 추가해주어야 한다. 이 설정을 통해 Nuxt ESlint는 커스텀이 가능한 상태가 되고 그래야 antfu의 설정을 적용할 수 있다. 이 설정이 없으면 ESlint의 콘솔에서 해당 설정이 빠졌다고 친절하게 알려준다.\n\nantfu의 ESlint 설정을 써보니 놀라운 게, `import` 코드의 순서도 자동으로 보장해주고, vue 템플릿 코드의 구조도 알아서 잡아주었다. Prettier로도 이게 불가능하진 않을 것 같은데, ESlint로 이정도까지 포맷팅이 가능하다면 ESlint만 있어도 충분하다는 생각이 든다.\n\n그동안 [Biome](https:\u002F\u002Fbiomejs.dev\u002F), [dprint](https:\u002F\u002Fdprint.dev\u002F) 등 Prettier & ESlint 조합보다 더 모던한 툴을 사용해보려고 애썼었다. Biome는 아직 vue 템플릿 코드에 대한 지원이 미비하고, dprint는 WebStrom에서 사용할 때 계속 에러가 나는 현상이 있었다. 결국 다시 이전에 쓰던 조합으로 돌아왔는데, 이젠 ESlint만 써도 될 것 같다는 생각이 든다.\n\nESlint의 설정은 아주 세세한 부분까지 설정이 가능한 것 같다. 나는 세세한 룰은 거의 파악하지 못했다. 아마 사용하면서 자동으로 포맷팅 되는 영역을 직접 파악하며 점차 알아가야 할 것 같다. 아래 링크는 antfu의 ESlint 설정을 볼 수 있는 웹 페이지다. 이런 식으로 설정을 공유하고 하나로 써서 코드 스타일을 통일하는 게 같은 언어, 같은 코드 베이스를 다루는 팀원과의 협업에서는 매우 중요할 것 같다.\n\n[ESLint Config Inspector](https:\u002F\u002Feslint-config.antfu.me\u002Fconfigs)","published",{"data":20,"body":21},{},{"type":22,"children":23},"root",[24,58,87,101,106,120,179,184,193,214,227,249,254],{"type":25,"tag":26,"props":27,"children":28},"element","p",{},[29,39,41,47,49,56],{"type":25,"tag":30,"props":31,"children":35},"a",{"href":32,"rel":33},"https:\u002F\u002Fnuxt.com\u002F",[34],"nofollow",[36],{"type":37,"value":38},"text","Nuxt",{"type":37,"value":40}," 프레임워크는 전용 ",{"type":25,"tag":30,"props":42,"children":45},{"href":43,"rel":44},"https:\u002F\u002Feslint.org\u002F",[34],[46],{"type":37,"value":10},{"type":37,"value":48}," 모듈을 제공한다. ",{"type":25,"tag":30,"props":50,"children":53},{"href":51,"rel":52},"https:\u002F\u002Feslint.nuxt.com\u002F",[34],[54],{"type":37,"value":55},"Nuxt ESlint",{"type":37,"value":57},"라는 것인데, 이번에 나를 위한 CMS를 구축하려고 대시보드 웹 앱을 Nuxt 프레임워크 기반으로 만들게 되면서 CLI로 프로젝트를 생성할 때 함께 설치하게 되었다.",{"type":25,"tag":26,"props":59,"children":60},{},[61,67,69,76,78,85],{"type":25,"tag":30,"props":62,"children":65},{"href":63,"rel":64},"https:\u002F\u002Fprettier.io\u002F",[34],[66],{"type":37,"value":13},{"type":37,"value":68},"만 써오던 나에게 ESlint의 설정은 생소하고 어려웠다. 그러다 ",{"type":25,"tag":30,"props":70,"children":73},{"href":71,"rel":72},"https:\u002F\u002Fantfu.me\u002F",[34],[74],{"type":37,"value":75},"antfu",{"type":37,"value":77},"가 만든 ",{"type":25,"tag":30,"props":79,"children":82},{"href":80,"rel":81},"https:\u002F\u002Fgithub.com\u002Fantfu\u002Feslint-config",[34],[83],{"type":37,"value":84},"ESlint configuration 레포지토리",{"type":37,"value":86},"를 알게 되었다.",{"type":25,"tag":26,"props":88,"children":89},{},[90,92,99],{"type":37,"value":91},"(antfu는 ",{"type":25,"tag":30,"props":93,"children":96},{"href":94,"rel":95},"https:\u002F\u002Fantfu.me\u002Fposts\u002Fwhy-not-prettier",[34],[97],{"type":37,"value":98},"자신의 글",{"type":37,"value":100},"에서 Prettier를 사용하지 않고 ESlint로 포맷팅까지 하는 이유에 대해 밝힌 적이 있다.)",{"type":25,"tag":26,"props":102,"children":103},{},[104],{"type":37,"value":105},"Nuxt ESlint가 제공하는 기본적인 설정에 antfu의 설정을 병합할 수 있다. 아래와 같은 코드로 설정이 가능하다.",{"type":25,"tag":107,"props":108,"children":114},"pre",{"className":109,"code":111,"language":112,"meta":113},[110],"language-typescript","\u002F\u002F eslint.config.mjs\n\n\u002F\u002F @ts-check\nimport { antfu } from '@antfu\u002Feslint-config'\nimport withNuxt from '.\u002F.nuxt\u002Feslint.config.mjs'\n\nexport default withNuxt(\n  antfu({\n    vue: true,\n    typescript: true,\n    formatters: {\n      css: true,\n      html: true,\n      markdown: 'prettier',\n    },\n  }),\n)\n","typescript","",[115],{"type":25,"tag":116,"props":117,"children":118},"code",{"__ignoreMap":113},[119],{"type":37,"value":111},{"type":25,"tag":26,"props":121,"children":122},{},[123,125,131,133,138,140,147,149,155,157,163,164,170,171,177],{"type":37,"value":124},"이건 나의 설정 코드를 그대로 옮긴 것인데, 사실 ",{"type":25,"tag":116,"props":126,"children":128},{"className":127},[],[129],{"type":37,"value":130},"vue",{"type":37,"value":132},", ",{"type":25,"tag":116,"props":134,"children":136},{"className":135},[],[137],{"type":37,"value":112},{"type":37,"value":139}," 같은 필드는 필요하지 않다고 생각했다. 그런데 ",{"type":25,"tag":30,"props":141,"children":144},{"href":142,"rel":143},"https:\u002F\u002Fwww.jetbrains.com\u002Fko-kr\u002Fwebstorm\u002F",[34],[145],{"type":37,"value":146},"WebStorm",{"type":37,"value":148},"의 저장 액션으로 ESlint fix를 아무리 설정해도 코드 포맷팅이 되질 않았다. 그래서 추가했고, ",{"type":25,"tag":116,"props":150,"children":152},{"className":151},[],[153],{"type":37,"value":154},"formatter",{"type":37,"value":156}," 필드 내부의 ",{"type":25,"tag":116,"props":158,"children":160},{"className":159},[],[161],{"type":37,"value":162},"css",{"type":37,"value":132},{"type":25,"tag":116,"props":165,"children":167},{"className":166},[],[168],{"type":37,"value":169},"html",{"type":37,"value":132},{"type":25,"tag":116,"props":172,"children":174},{"className":173},[],[175],{"type":37,"value":176},"markdown",{"type":37,"value":178},"과 같은 필드들은 사용하지 않으면 해당 언어의 포맷팅이 전혀 되지 않으므로, 해당 언어로 코드를 쓴다면 무조건 추가해주는 게 좋다.",{"type":25,"tag":26,"props":180,"children":181},{},[182],{"type":37,"value":183},"이 과정에서 계속 ESlint의 컴파일 에러(?)가 나곤 했는데 위 설정과 더불어 꼭 해줘야 하는 설정이 있었는데 빼먹어서 그런 거였다. Nuxt의 전체적인 설정을 아래와 같이 해주어야 한다.",{"type":25,"tag":107,"props":185,"children":188},{"className":186,"code":187,"language":112,"meta":113},[110],"\u002F\u002F nuxt.config.ts\nimport tailwindcss from '@tailwindcss\u002Fvite'\n\n\u002F\u002F https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fapi\u002Fconfiguration\u002Fnuxt-config\nexport default defineNuxtConfig({\n  modules: [\n    '@nuxt\u002Ffonts',\n    '@nuxt\u002Ftest-utils',\n    '@pinia\u002Fnuxt',\n    'pinia-plugin-persistedstate\u002Fnuxt',\n    '@nuxt\u002Feslint',\n  ],\n  devtools: { enabled: true },\n  css: ['~\u002Fassets\u002Fcss\u002Fmain.css'],\n  compatibilityDate: '2025-05-15',\n  vite: { plugins: [tailwindcss()] },\n  eslint: {\n    config: {\n      standalone: false,\n    },\n  },\n})\n",[189],{"type":25,"tag":116,"props":190,"children":191},{"__ignoreMap":113},[192],{"type":37,"value":187},{"type":25,"tag":26,"props":194,"children":195},{},[196,198,204,206,212],{"type":37,"value":197},"이 코드는 내 프로젝트의 Nuxt configuration 코드를 그대로 옮긴 것이다. 이 코드의 마지막 부분에 eslint 필드로 추가된 ",{"type":25,"tag":116,"props":199,"children":201},{"className":200},[],[202],{"type":37,"value":203},"standalone",{"type":37,"value":205}," 필드값을 ",{"type":25,"tag":116,"props":207,"children":209},{"className":208},[],[210],{"type":37,"value":211},"false",{"type":37,"value":213}," 로 꼭 추가해주어야 한다. 이 설정을 통해 Nuxt ESlint는 커스텀이 가능한 상태가 되고 그래야 antfu의 설정을 적용할 수 있다. 이 설정이 없으면 ESlint의 콘솔에서 해당 설정이 빠졌다고 친절하게 알려준다.",{"type":25,"tag":26,"props":215,"children":216},{},[217,219,225],{"type":37,"value":218},"antfu의 ESlint 설정을 써보니 놀라운 게, ",{"type":25,"tag":116,"props":220,"children":222},{"className":221},[],[223],{"type":37,"value":224},"import",{"type":37,"value":226}," 코드의 순서도 자동으로 보장해주고, vue 템플릿 코드의 구조도 알아서 잡아주었다. Prettier로도 이게 불가능하진 않을 것 같은데, ESlint로 이정도까지 포맷팅이 가능하다면 ESlint만 있어도 충분하다는 생각이 든다.",{"type":25,"tag":26,"props":228,"children":229},{},[230,232,239,240,247],{"type":37,"value":231},"그동안 ",{"type":25,"tag":30,"props":233,"children":236},{"href":234,"rel":235},"https:\u002F\u002Fbiomejs.dev\u002F",[34],[237],{"type":37,"value":238},"Biome",{"type":37,"value":132},{"type":25,"tag":30,"props":241,"children":244},{"href":242,"rel":243},"https:\u002F\u002Fdprint.dev\u002F",[34],[245],{"type":37,"value":246},"dprint",{"type":37,"value":248}," 등 Prettier & ESlint 조합보다 더 모던한 툴을 사용해보려고 애썼었다. Biome는 아직 vue 템플릿 코드에 대한 지원이 미비하고, dprint는 WebStrom에서 사용할 때 계속 에러가 나는 현상이 있었다. 결국 다시 이전에 쓰던 조합으로 돌아왔는데, 이젠 ESlint만 써도 될 것 같다는 생각이 든다.",{"type":25,"tag":26,"props":250,"children":251},{},[252],{"type":37,"value":253},"ESlint의 설정은 아주 세세한 부분까지 설정이 가능한 것 같다. 나는 세세한 룰은 거의 파악하지 못했다. 아마 사용하면서 자동으로 포맷팅 되는 영역을 직접 파악하며 점차 알아가야 할 것 같다. 아래 링크는 antfu의 ESlint 설정을 볼 수 있는 웹 페이지다. 이런 식으로 설정을 공유하고 하나로 써서 코드 스타일을 통일하는 게 같은 언어, 같은 코드 베이스를 다루는 팀원과의 협업에서는 매우 중요할 것 같다.",{"type":25,"tag":26,"props":255,"children":256},{},[257],{"type":25,"tag":30,"props":258,"children":261},{"href":259,"rel":260},"https:\u002F\u002Feslint-config.antfu.me\u002Fconfigs",[34],[262],{"type":37,"value":263},"ESLint Config Inspector",1777192769760]