<?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>Express on Kelly Dev</title><link>https://kelly-chui.github.io/tags/express/</link><description>Recent content in Express on Kelly Dev</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 08 Aug 2026 16:27:42 +0900</lastBuildDate><atom:link href="https://kelly-chui.github.io/tags/express/index.xml" rel="self" type="application/rss+xml"/><item><title>dev-data-server-light. 0. Concept</title><link>https://kelly-chui.github.io/posts/devbox-light-server-00-concept/</link><pubDate>Wed, 24 Jun 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-00-concept/</guid><description>&lt;h2 id="시작하기"&gt;시작하기&lt;/h2&gt;
&lt;p&gt;iOS 프로젝트를 진행하다 보면 생각보다 자주 간단한 API 서버가 필요해진다. 회원가입 화면을 만들거나, 목록 조회 기능을 붙이거나, 이미지 업로드를 테스트할 때도 서버가 필요하다. 물론 실제 백엔드를 구축할 수도 있고, Supabase나 Firebase 같은 BaaS를 사용할 수도 있다. 하지만 작은 개인 프로젝트나 프로토타입 단계에서는 좋은 설계는 아니라고 생각한다.&lt;/p&gt;
&lt;p&gt;특히 처음부터 특정 서비스에 의존하기 시작하면, 정작 내가 만들고 싶은 앱의 기능보다 인프라 설정과 서비스 사용법을 익히는 데 더 많은 시간을 쓰게 된다. 앱 개발을 위한 도구가 필요했는데, 어느 순간 도구를 사용하기 위한 공부를 하고 있는 상황이 되는 것이다.&lt;/p&gt;</description></item><item><title>dev-data-server-light. 1. System</title><link>https://kelly-chui.github.io/posts/devbox-light-server-01-system-module/</link><pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-01-system-module/</guid><description>&lt;p&gt;프로젝트에서 제일 간단한 기능과 구조를 가지기 때문에 가장 먼저 System 모듈을 만들었다.&lt;/p&gt;
&lt;h2 id="system-모듈의-기능"&gt;System 모듈의 기능&lt;/h2&gt;
&lt;p&gt;System 모듈이 제공하는 기능은 단 두 가지뿐이다.&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /system/status
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GET /system/uptime&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;status&lt;/code&gt;는 서버가 정상적으로 동작하는지 알려주고, &lt;code&gt;uptime&lt;/code&gt;은 서버가 실행된 이후 얼마나 시간이 지났는지를 반환한다.&lt;/p&gt;
&lt;p&gt;비즈니스 로직도 없고, 데이터베이스도 필요하지 않으며, 파일 시스템도 다루지 않기 때문에 구조를 검증하는데 가장 좋다고 판단했다.&lt;/p&gt;
&lt;h2 id="구조-검증"&gt;구조 검증&lt;/h2&gt;
&lt;p&gt;System 모듈을 만들면서 확인하고 싶었던 것은 기능이 아니라 구조였다. 당시에 생각했던 구조는 다음과 같았다.&lt;/p&gt;</description></item><item><title>dev-data-server-light. 2. Database CRUD</title><link>https://kelly-chui.github.io/posts/devbox-light-server-02-db-crud/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/devbox-light-server-02-db-crud/</guid><description>&lt;p&gt;시스템 모듈을 만든 이후에, 아마도 가장 많이 쓰게 될 DB 모듈을 만들었다.&lt;/p&gt;
&lt;img loading="lazy" src="https://kelly-chui.github.io/posts/devbox-light-server-02-db-crud/image-001-optimized-image.webp"&gt;&lt;p&gt;당시에는 구조도 시스템 모듈과 동일하다고 생각했다. Router가 DB 인터페이스에 의존하고, 이를 구현한 구현체를 주입받는 방식으로 DIP와 DI를 동시에 적용했다.&lt;/p&gt;
&lt;p&gt;이 과정 중에서&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;naming bias 해결하기&lt;/li&gt;
&lt;li&gt;id 생성 책임 위치&lt;/li&gt;
&lt;li&gt;기존 아키텍처의 문제점 -&amp;gt; 서비스 레이어 도입해서 해결하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;에 대한 고민을 했다. 아래는 고민과 해결 과정이다.&lt;/p&gt;
&lt;h2 id="crud부터-만들기"&gt;CRUD부터 만들기&lt;/h2&gt;
&lt;p&gt;Mock Server라도 최소한 CRUD는 제공해야 한다. 처음부터 구현체를 만들 필요는 없으므로, 라우터와 인터페이스까지만 만들도록 프롬프트를 작성했다.&lt;/p&gt;</description></item><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>TIL. May 21, 2026</title><link>https://kelly-chui.github.io/til/2026/til-2026-05-21/</link><pubDate>Fri, 22 May 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/til/2026/til-2026-05-21/</guid><description>&lt;h1 id="요약"&gt;요약&lt;/h1&gt;
&lt;p&gt;LeetCode 문제를 풀면서 자료의 표현 방식이 성능에 큰 영향을 준다는 점을 다시 확인했다. 또한 Express의 &lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;, &lt;code&gt;next()&lt;/code&gt;가 어떻게 협력하여 요청을 처리하는지 파이프라인 관점에서 정리했다.&lt;/p&gt;
&lt;h2 id="배운-것"&gt;배운 것&lt;/h2&gt;
&lt;h3 id="leetcode-3043-최적화"&gt;LeetCode 3043 최적화&lt;/h3&gt;
&lt;p&gt;LeetCode 3043번 문제를 풀었다. 두 정수형 배열에 있는 원소들 중 임의의 두 수를 선택했을 때, 공통의 일치하는 prefix의 최대 길이를 구하는 문제였다.&lt;/p&gt;
&lt;p&gt;우선, 각 배열의 길이가 최대 50000이기 때문에, 가능한 모든 쌍을 만들어서 크기를 구하는 방식으로 하면 시간복잡도가 O(n²)이 되어서 문제를 제 시간 안에 해결하기 힘들다. (n은 둘 중 긴 배열의 원소의 개수)&lt;/p&gt;</description></item><item><title>TIL. May 19, 2026</title><link>https://kelly-chui.github.io/til/2026/til-2026-05-19/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/til/2026/til-2026-05-19/</guid><description>&lt;h1 id="요약"&gt;요약&lt;/h1&gt;
&lt;p&gt;Express의 핵심 구성 요소와 요청 처리 흐름을 정리했다. 특히 Middleware가 특정 객체가 아니라 요청 처리 과정에서 동작하는 함수들의 역할이라는 점과, Express가 함수형 체인이 아닌 공유 객체(&lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;)를 사용하는 파이프라인 구조라는 점을 이해하게 되었다.&lt;/p&gt;
&lt;h2 id="배운-것"&gt;배운 것&lt;/h2&gt;
&lt;h3 id="오늘-공부한-내용"&gt;오늘 공부한 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Express의 App, Router, Middleware&lt;/li&gt;
&lt;li&gt;Express의 동작&lt;/li&gt;
&lt;li&gt;Express의 간단한 내부 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="express의-핵심-구성-요소"&gt;Express의 핵심 구성 요소&lt;/h2&gt;
&lt;p&gt;처음에는 Express를 iOS의 MVC에서 View를 제거한 형태 정도로 생각했다. Route Handler가 Controller 역할을 하는 것이라고 생각했다.
Express를 구성하는 핵심 요소는 크게 다음과 같다.&lt;/p&gt;</description></item></channel></rss>