<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>File Storage on Kelly Dev</title><link>https://kelly-chui.github.io/tags/file-storage/</link><description>Recent content in File Storage on Kelly Dev</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 10 Aug 2026 17:42:56 +0900</lastBuildDate><atom:link href="https://kelly-chui.github.io/tags/file-storage/index.xml" rel="self" type="application/rss+xml"/><item><title>dev-data-server-light. 4. File Storage</title><link>https://kelly-chui.github.io/posts/devbox-light-server-04-file-storage/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-04-file-storage/</guid><description>&lt;p&gt;DB 모듈을 마무리하고, 다음으로 구현한 것은 File Storage 모듈이다. DB 모듈과 크게 다르지 않을 것이라고 생각했다. 우선 CRUD 비슷한 메소드를 처리하고, 구조는 DB 모듈에서 충분히 다듬었기 때문이다. 하지만 실제로는 DB보다 인터페이스를 어떻게 정의할 것인지에 대한 고민이 더 많았다.&lt;/p&gt;
&lt;h2 id="인터페이스-만들기"&gt;인터페이스 만들기&lt;/h2&gt;
&lt;p&gt;처음 작성한 프롬프트는 다음과 같다.&lt;/p&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Create the initial FileStorage contract in src/storage.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Requirements:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Define a FileStorage interface.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- The interface is responsible only for storing and retrieving binary file contents.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- It should not manage file metadata, ids, ownership, permissions, or database records.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Use Buffer for file contents.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Include only the minimal operations required for file storage:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - save(path: string, content: Buffer): Promise&amp;lt;void&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - read(path: string): Promise&amp;lt;Buffer&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - delete(path: string): Promise&amp;lt;void&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; - exists(path: string): Promise&amp;lt;boolean&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Add minimal domain errors if necessary.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;For this step, create only the contract and related types.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Do not implement any storage implementation yet.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;로컬 파일 시스템을 의식하고 있어서 의심 없이 &lt;code&gt;path&lt;/code&gt;라는 이름을 사용했는데, 이 때문에 인터페이스가 구현체를 닮아버리게 되었다. &lt;code&gt;path&lt;/code&gt;는 과연 인터페이스를 대표할 수 있는 네이밍인가?&lt;/p&gt;</description></item><item><title>devbox-light-server (5). Auth</title><link>https://kelly-chui.github.io/posts/devbox-light-server-05-auth/</link><pubDate>Sat, 08 Aug 2026 16:27:42 +0900</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-05-auth/</guid><description>&lt;h2 id="auth-설계하기"&gt;Auth 설계하기&lt;/h2&gt;
&lt;p&gt;개발용 목서버에서 실제 서비스 수준의 인증 시스템을 구현하지 않고도, 클라이언트에서 사용하는 로그인과 인증 흐름을 테스트할 수 있도록 Auth 기능을 추가했다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;로그인 요청을 보낸다.&lt;/li&gt;
&lt;li&gt;access token을 발급받는다.&lt;/li&gt;
&lt;li&gt;Authorization 헤더에 token을 실어 요청한다.&lt;/li&gt;
&lt;li&gt;현재 로그인된 유저를 확인한다.&lt;/li&gt;
&lt;li&gt;로그아웃으로 token을 무효화한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JWT, 비밀번호, OAuth, Refresh Token, 보안 같은 기능은 의도적으로 제외했다. 프로덕션 인증 로직을 구현하려는 글이 아니라, 개발용 Mock 서버에서 클라이언트의 로그인, 인증, 로그아웃 흐름을 확인하는 것이 목적이기 때문이다.&lt;/p&gt;
&lt;h3 id="최소한의-auth-구조"&gt;최소한의 Auth 구조&lt;/h3&gt;
&lt;p&gt;첫 단계에서는 &lt;code&gt;DatabaseService&lt;/code&gt;를 그대로 사용했다. &lt;code&gt;users&lt;/code&gt; 컬렉션을 만들어서 그 레코드 ID를 유저 ID로 사용해 로그인할 수 있도록 했다.&lt;/p&gt;</description></item><item><title>devbox-light-server (6). Json 영속성</title><link>https://kelly-chui.github.io/posts/devbox-light-server-06-persistence/</link><pubDate>Mon, 10 Aug 2026 14:14:08 +0900</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-06-persistence/</guid><description>&lt;p&gt;MVP의 마지막 기능으로 서버를 재시작해도 데이터가 사라지지 않도록 영속성을 추가했다.&lt;/p&gt;
&lt;p&gt;이번 포스트에서는 devbox-light-server에 DB, Storage, Token Storage에서 영속성을 가진 저장 방식을 구현해서 in-memory 대신 사용할 수 있도록 만든다.&lt;/p&gt;
&lt;p&gt;프로토타입이었던 dev-data-server에서는 시작부터 SQL 의존성을 가지고 시작했지만, devbox-light-server는 아주 얇은 in-memory 구현만 가지고 있다.&lt;/p&gt;
&lt;p&gt;MVP 마지막에 영속성을 넣는 이유는 서버를 구현할 때, 특정 구현에 휘둘리지 않기 위해서다. dev-data-server에서 SQL을 빠르게 들여와서, 결국 프로젝트가 SQL 래퍼가 된 느낌이 있다.&lt;/p&gt;
&lt;p&gt;당연히 Service 레이어는 Repository의 인터페이스에만 의존하므로 어떤 방법을 쓰든 단순히 구현체만 갈아 끼우고, 의존성 생성, 연결 부분을 제외하고는 어떠한 코드에도 변화가 없어야 한다.&lt;/p&gt;</description></item><item><title>devbox-light-server (7). Dockerizing, MVP 마무리</title><link>https://kelly-chui.github.io/posts/devbox-light-server-07-dockerizing/</link><pubDate>Mon, 10 Aug 2026 17:42:56 +0900</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-07-dockerizing/</guid><description>&lt;h2 id="도커라이징"&gt;도커라이징&lt;/h2&gt;
&lt;p&gt;영속성까지 추가했으므로 이제 서버를 하나의 실행 단위로 묶을 차례다.&lt;/p&gt;
&lt;p&gt;지금까지는 로컬에서 Node.js를 설치하고, 의존성을 설치하고, TypeScript를 빌드한 다음 서버를 실행했다. 혼자 개발할 때는 큰 문제가 없지만, 이 서버를 다른 환경에서 실행하려면 매번 같은 준비 과정을 반복해야 한다.&lt;/p&gt;
&lt;p&gt;도커라이징은 이 과정을 이미지 안에 고정하는 작업이다. 이 프로젝트에서는 Express 서버를 빌드하고, 실행에 필요한 파일만 담은 뒤, &lt;code&gt;.data&lt;/code&gt; 디렉터리를 기준으로 데이터를 유지할 수 있도록 구성했다.&lt;/p&gt;
&lt;h3 id="dockerfile-작성하기"&gt;Dockerfile 작성하기&lt;/h3&gt;
&lt;p&gt;Dockerfile은 두 단계로 나눴다.&lt;/p&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:24-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; package.json package-lock.json ./&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; npm ci&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; tsconfig.json tsconfig.build.json ./&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; src ./src&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; npm run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;첫 번째 &lt;code&gt;build&lt;/code&gt; 단계에서는 개발 의존성을 포함해서 설치하고 TypeScript를 빌드한다. &lt;code&gt;npm run build&lt;/code&gt;를 실행하면 &lt;code&gt;dist&lt;/code&gt; 디렉터리가 만들어진다.&lt;/p&gt;</description></item></channel></rss>