page-cover
개발
Vercel에서 Local File 사용해보기
sooros5132-avatarsooros51324/4/2023Vercel Next.js
Vercel의 호스팅 서비스를 이용하고 있는데 속도를 높이기 위해 db까진 사용하지 않고 notion api 결과를 잠깐 캐시할 정도로만 사용하고 싶었다.
vercel에서 파일 시스템을 사용하는 방법과 에러의 정보들을 적어놓았다.
  • /notion/cache/data.json - 배포 전 부터 이미 있는 파일
  • /notion/cache/[uuid].json - 배포가 완료된 후 유동적으로 새로 쓰고 싶은 파일
Vercel에 배포하기 전 부터 있던 data.json파일을 읽을 수 있지만 배포가 완료된 프로젝트에서는 프로젝트 폴더에서 파일을 쓸 수 없다. nftTracing: true을 사용하면 된다고 하는데 안됨. 그냥 안됨. 몇 시간을 그냥 날렸다.
bash
[Error: EROFS: read-only file system, open '/var/task/notion/cache/c9f96f81-4f4a-4f4a-a7d4-dfca7ade2a09.json'] {
  errno: -30,
  code: 'EROFS',
  syscall: 'open',
  path: '/var/task/notion/cache/c9f96f81-4f4a-4f4a-a7d4-dfca7ade2a09.json'
}
그래서 console을 찍어보니 read-only file system이라 나온다.
구글링을 더 해보니 나와 비슷한 사람이 있는지 vercel은 UNIX 환경이라 /tmp(temporary  storage)폴더에 쓰면 된다고 한다. 그래서 바로 적용해보니 됐다. 하지만tmp폴더는 영원히 저장되는 곳이 아니기 때문에 좋은 방법은 아니긴 하다. 테스트를 해보니 파일이 10분정도 뒤에 지워졌다. 짧은 캐시 목적이니 상관없을 거 같아서 적용
테스트 목적이니 GET으로 테스트를 해봤다. next-connect 라이브러리를 썼지만 이건 안써도 된다.
/api/write.ts
typescript
import type { NextApiRequest, NextApiResponse } from 'next';
import nc from 'next-connect';
import path from 'path';
import { promises as fs } from 'fs';

const CACHE_PATH =
  process.env.VERCEL === '1'
    ? path.join('/tmp', 'notion-blog-kit', 'notion', 'cache')
    : path.join(process.cwd(), 'notion', 'cache');

const handler = nc()
	.get(async (req: NextApiRequest, res: NextApiResponse<any>) => {
	  const { filePath, content } = req.query;
	
	  if (typeof filePath === 'string' && typeof content === 'string') {
	    try {
	      await fs.access(CACHE_PATH, (fs.constants || fs).R_OK | (fs.constants || fs).W_OK);
	    } catch {
	      await fs.mkdir(CACHE_PATH, { recursive: true });
	    }
	    const writePath = path.join(CACHE_PATH, filePath);
	
	    await fs.writeFile(writePath, content, 'utf-8');
	
	    res.status(200).json('write success');
	  }
	  res.status(400);
	});

export default handler;
CACHE_PATH에 Vercel 환경인지 검사를 하고 맞다면 /tmp 아니라면 프로젝트 폴더에 cache를 하게 했다.
/api/read.ts
typescript
import type { NextApiRequest, NextApiResponse } from 'next';
import nc from 'next-connect';
import path from 'path';
import { promises as fs } from 'fs';

const CACHE_PATH =
  process.env.VERCEL === '1'
    ? path.join('/tmp', 'notion-blog-kit', 'notion', 'cache')
    : path.join(process.cwd(), 'notion', 'cache');

const handler = nc()
	.get(async (req: NextApiRequest, res: NextApiResponse<any>) => {
	  const { filePath } = req.query;
	
	  if (typeof filePath === 'string') {
	    const readPath = path.join(CACHE_PATH, filePath);
	
	    const content = await fs.readFile(readPath, 'utf-8');
	
	    res.status(200).json(content);
	  }
	  res.status(400);
	});

export default handler;
query로 오는 filePath 경로로 파일을 읽어서 전달
간단하게 cache-test.json 파일에 {"test": true}를 작성해보려고 한다.
/api/write.ts
Query
  • filePath - cache-test.json
  • content - {"test": true}
파일 쓰기 성공
/api/read.ts
Query
  • filePath - cache-test.json
읽기 성공