Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions apps/web/src/shared/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@ import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as scheme from "./scheme";

// biome-ignore lint: 환경변수는 서버에서만 접근 가능하므로 안전하다.
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle({ client: sql, schema: scheme });
// DB 인스턴스를 처음 사용할 때까지 초기화를 지연합니다.
// 모듈 import 시점에 neon()을 호출하면 환경변수가 아직 준비되지 않은
// 환경(테스트, 일부 번들러)에서 에러가 발생하기 때문입니다.
type DB = ReturnType<typeof drizzle<typeof scheme>>;
let _db: DB | undefined;

function getDb(): DB {
if (!_db) {
// biome-ignore lint: 환경변수는 서버에서만 접근 가능하므로 안전하다.
const sql = neon(process.env.DATABASE_URL!);
_db = drizzle({ client: sql, schema: scheme });
}
return _db;
}

// Proxy를 통해 db.query, db.insert 등 기존 호출부를 그대로 유지하면서
// 실제 접근이 일어날 때 getDb()로 초기화를 트리거합니다.
export const db = new Proxy({} as DB, {
get(_, prop) {
return getDb()[prop as keyof DB];
},
});
Comment on lines +22 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Proxy에 get 트랩만 구현되어 있어 일부 연산에서 예기치 않은 동작 가능

현재 Proxy는 get 트랩만 구현되어 있습니다. 대부분의 DB 사용 케이스(db.query, db.insert 등)에서는 문제없지만, 다음과 같은 코드가 있으면 의도와 다르게 동작합니다:

  • 'query' in db → 항상 false (빈 객체 기준)
  • Object.keys(db) → 빈 배열 반환

현재 코드베이스에서 이런 패턴이 없다면 문제없지만, 필요시 추가 트랩을 구현할 수 있습니다.

🔧 더 완전한 Proxy 구현 (필요시)
 export const db = new Proxy({} as DB, {
   get(_, prop) {
     return getDb()[prop as keyof DB];
   },
+  has(_, prop) {
+    return prop in getDb();
+  },
+  ownKeys() {
+    return Reflect.ownKeys(getDb());
+  },
+  getOwnPropertyDescriptor(_, prop) {
+    return Object.getOwnPropertyDescriptor(getDb(), prop);
+  },
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/shared/db/index.ts` around lines 22 - 26, The Proxy exported as
db only implements the get trap which causes operations like "'query' in db" and
"Object.keys(db)" to behave incorrectly; update the Proxy created around {} as
DB to also implement at least the has and ownKeys (and corresponding
getOwnPropertyDescriptor) traps so reflective operations consult getDb() results
(use getDb() to check property existence in has, return the target object's keys
via ownKeys by reflecting on the current DB instance, and provide property
descriptors in getOwnPropertyDescriptor). Refer to the db Proxy, getDb(), and DB
type when adding these traps.

4 changes: 1 addition & 3 deletions apps/web/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: "jsdom",
env: {
DATABASE_URL: "postgresql://test:test@localhost:5432/test",
},
setupFiles: ["./vitest.setup.ts"],
},
});
15 changes: 15 additions & 0 deletions apps/web/vitest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { vi } from "vitest";

// 테스트 환경(jsdom)에서는 실제 DB 연결이 없으므로
// @/shared/db 모듈 전체를 가짜(mock)로 대체합니다.
// 각 테스트 파일에서 vi.mocked(db.insert).mockResolvedValue(...) 등으로
// 원하는 반환값을 설정할 수 있습니다.
vi.mock("@/shared/db", () => ({
db: {
query: {},
insert: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
select: vi.fn(),
},
}));
Loading