Fresh와의 안 좋은 첫 만남: @fedify/fresh 제작기 - 1
개발곰 @gaebalgom@hackers.pub
Fresh?
Deno Fresh라는 프레임워크를 들어보셨나요? Deno 팀에서 개발하고 밀어주는 프레임워크인데요. Fedify 프로젝트의 일환으로 Create @fedify/fresh package for Fresh 2.0 integration 이슈 작업을 하면서 처음으로 접해 봤답니다.
레몬 로고답게 보기에는 예쁘지만 직접 찍먹하니 여러 문제를 겪게 되어 정리해 보았습니다.
작업 배경
Create @fedify/fresh package for Fresh 2.0 integration 작업에는 크게 2가지 배경이 있습니다.
- Fresh가 2.X 대로 진입하면서 기존 코드랑 호환되지 않는 방향으로 제작됨.
- Fedify의 패키지들이 정리 되면서
/x/아래 있던 코드들을 제거하고, 통합을 위한 별도 패키지를 제작하는 방향으로 정리 됨.
그래서 Fedify 2.0.0 부터는 Fresh 2.0을 지원하는 새로운 패키지를 만들기로 결정했습니다.
통합을 위한 패키지를 만들 때 다음과 같은 순서로 진행합니다.
examples아래에 해당 패키지를 이용한 프로젝트 만들고 실행해보기- Federation 관련 파일 및 미들웨어 제작
- 잘 작동하는 것을 확인하면 별도 패키지로 분리
- PR을 올리고, 코드를 점검받고, 문서화를 하고, init 커맨드에 추가하고...
현재 2까지 진행되어 별도 패키지로 분리하는 작업을 앞두고 있는데요. 그 전에 밞은 이슈들을 정리하면 좋을 것 같아서 정리하게 되었습니다.
밟았던 이슈들
(해결됨) example 폴더 아래에 프로젝트가 제대로 작동하지 않아요.
deno run -Ar jsr:@fresh/init 이라는 명령어로 fresh는 프로젝트를 만듭니다. fedify/examples 아래에 평상시처럼 fresh로 프로젝트를 만들고, 개발 서버를 실행(deno task dev)을 하려고 하니 안됩니다?
error: Config file must be a member of the workspace.
Config: file:///home/dodok8/Development/fedify/examples/fresh/deno.json
Workspace: file:///home/dodok8/Development/fedify/
아하, workspace 지정이 안되어 있나 보네요. workspace에 지정해서 넣어줬습니다.
...
╭ Warning
│
│ Ignored build scripts for packages:
│ npm:workerd@1.20251011.0
│ npm:sharp@0.33.5
│
│ Lifecycle scripts are only supported when using a `node_modules` directory.
│ Enable it in your deno config file:
│ "nodeModulesDir": "auto"
╰─
Task dev vite
error: Uncaught Error: UNHANDLED PROMISE REJECTION
at Process.<anonymous> (file:///home/dodok8/.cache/deno/npm/registry.npmjs.org/vite/7.1.12/bin/vite.js:12:11)
...
길긴 하지만, 중간에 있는 Warning이 친절하게 강조되어 있네요. nodeMoudlesDir을 auto로 바꿔줘야 된다고 합니다. 이게 뭐하는 옵션인지 Deno 공식 문서를 봐보면, 이 값을 "auto"로 설정하면 deno cache 대신에 node_modules에서 패키지들을 가져오게 된다고 합니다. 다행히 fedify의 다른 빌드 설정에 영향은 없었습니다. 제 컴퓨터의 node_modules가 더 커졌을 뿐.
프론트엔드가 안보인다! (해결 안됨)
아무튼 이제 기대를 안고 deno task dev를 시작해 봤습니다. Fresh와 Preact와 처음으로 만나는 순간이었습니다.
그런데...

아무것도 안 보입니다? 이상하다? 공식 문서랑 별도 저장소에서 했을 때는 카운터 예제가 보였는데? 찾아보니 저랑 같은 증상을 가지신 분이 Deno 공식 디코에 있었습니다. 개발자 말로는 Multiple Copy의 Preact가 설치되면 이런 현상이 발생할 수 있다 하더라고요. Deno에서는 2.5.4에서 해결된 버그라고 하고요. 근데 저는 2.5.6입니다. 설치 캐쉬를 날려 봐도 여전히 아무것도 안 보입니다. 뭔가 node_modules 관련해서 뭔가 꼬인 것 같은데 원인을 알수가 없네요. 수동으로 node_modules에서 preact를 지워도 import 에러 대신에 빈화면으로 실행되는 걸 보니 참으로 알 수 없는 노릇입니다.
혹시 저와 Deno 디코의 닉네임만 알고 있는 한 외국인 분을 도울 수 있는 분은 방법을 알려주시면 감사하겠습니다.
Fedify 미들웨어를 만드니 서버가 실행이 안 됨. (해결 완료)
미들웨어 자체는 타입이 변한 것 말고는 크게 달라진게 없어서 이전 코드에서 상당 부분을 가져다 쓴 다음, 타입과 인자만 Fresh 2.0에 맞게 바꿔줬습니다. 타입 에러를 해결하고 나서 실행했습니다. 정상적으로 실행 됩니다. 이제 접속해 봅시다. 빈화면이 반겨 주겠죠.

아니네요.
다행히 Claude Code와의 협력을 통해서 해결했습니다. 원리는 다음과 같았습니다.
- Fresh 2.0은 개발 서버로 vite를 사용함.
- 이때 미들웨어는, Vite의 SSR Module Runner가 처리함. 이 녀석은 CJS를 지원 안함.(대신 HMR 같은 여러 기능들이 딸려 있습니다.)
- 그런데 Fedify는 CJS 의존성이 있음.
- 펑
다행히도, 개발서버에서 특정 디펜더시와 그 디펜던시들을 Vite를 통하지 않고 외부에서 돌리도록 하는 옵션이 있었습니다. HMR이 안되긴 하겠지만, @fedify/fresh를 만들면서 실시간으로 @fedify/fedify를 수정할 일은 없을 테니까요.
아무튼 이제 Dev Server는 잘 돌아 갔습니다.

미들웨어도 잘 돌아가네요.
자 그럼 빌드 후 deno task start를 해볼까요?
error: Uncaught (in promise) Error: Cannot find module './llhttp/llhttp-wasm.js'
Require stack:
- /home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs
at Module._resolveFilename (node:module:633:15)
at Module._load (node:module:496:27)
at Module.require (node:module:694:19)
at require (node:module:828:16)
at lazyllhttp (file:///home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs:14671:67)
at file:///home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs:14714:21
안 되네요! 원인은 마찬가지로 cjs였습니다. build.rollupOptions의 external을 건드리니 해결이 되었습니다.
export default defineConfig({
plugins: [fresh()],
ssr: {
external: [
"@fedify/fedify",
],
},
build: {
rollupOptions: {
external: [
"@fedify/fedify",
],
},
},
});
최종적인 vite.config.ts 입니다.
이제 패키지 분리 작업에 들어가는데, 혹시 패키지 분리 작업하다가 또 버그 밟거나, 패키지 publish 후에 외부에서 사용하려고 하는데 실패한다면 제작기 2로 돌아오겠습니다. 안 돌아오길 기원해주세요.