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
40 changes: 40 additions & 0 deletions apps/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# syntax=docker/dockerfile:1

#
# 빌드 컨텍스트 = 저장소 루트 (packages/* 와 pnpm-lock.yaml 접근 필요):
# docker buildx build --platform linux/arm64 -f apps/server/Dockerfile -t <tag> .
#
# EC2가 Graviton(ARM)이므로 반드시 linux/arm64 로 빌드한다.
# x86 로 빌드하면 인스턴스에서 실행되지 않는다.

FROM node:22.13.0-slim AS base
# Prisma 엔진이 요구하는 OpenSSL, 아웃바운드 HTTPS(SMTP/OAuth/ECR)용 CA 인증서
RUN apt-get update \
&& apt-get install -y --no-install-recommends openssl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# corepack 번들 버전의 서명키가 오래돼 최신 pnpm 검증에 실패하므로 npm 으로 직접 설치.
RUN npm install -g pnpm@10.15.0
WORKDIR /app

# 워크스페이스 전체 복사 (.dockerignore 가 node_modules/dist/.env 등을 제외).
# frozen-lockfile 검증을 위해 모든 워크스페이스 package.json 이 필요하므로 전체 복사한다.
COPY . .

# server 서브트리 의존성만 설치 (web/extension 미포함). 락파일 고정.
RUN pnpm install --frozen-lockfile --filter server...

# Prisma 클라이언트 생성. generate 는 DB 연결이 불필요하지만
# prisma.config.ts 가 process.env.DATABASE_URL 을 읽으므로 더미 값을 준다.
RUN DATABASE_URL="postgresql://user:pass@localhost:5432/db?schema=public" \
pnpm --filter server exec prisma generate

# webpack 번들 빌드 → apps/server/dist/main.js (단일 파일, @dookmark/* 인라인)
RUN pnpm --filter server build

ENV NODE_ENV=production
ENV PORT=3001
WORKDIR /app/apps/server
EXPOSE 3001

# 컨테이너는 평문 HTTP(:3001)만 처리한다. TLS 는 앞단 ALB 가 종단.
CMD ["node", "dist/main.js"]
5 changes: 3 additions & 2 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"build": "nest build --webpack --webpackPath webpack.config.js",
"dev": "nest start --watch",
"start": "nest start",
"start:dev": "nest start --watch",
Expand Down Expand Up @@ -83,7 +83,8 @@
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.7.3"
"typescript": "^5.7.3",
"webpack-node-externals": "^3.0.0"
},
"jest": {
"moduleFileExtensions": [
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BookmarkModule } from './bookmark/bookmark.module';
import { PrismaExceptionFilter } from './common/filters/prisma-exception.filter';
import { RedisModule } from './common/redis/redis.module';
import { validateEnv } from './config/env.validation';
import { HealthController } from './health/health.controller';
import { PrismaModule } from './prisma/prisma.module';
import { SessionModule } from './session/session.module';
import { UserModule } from './user/user.module';
Expand All @@ -33,7 +34,7 @@ import { UserModule } from './user/user.module';
]),
ScheduleModule.forRoot(),
],
controllers: [AppController],
controllers: [AppController, HealthController],
providers: [
AppService,
{
Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/health/health.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { SkipThrottle } from '@nestjs/throttler';

// ALB 타깃 그룹 헬스체크용 경량 liveness 엔드포인트.
@SkipThrottle()
@Controller('health')
export class HealthController {
@Get()
check() {
return { status: 'ok', timestamp: new Date().toISOString() };
}
}
29 changes: 29 additions & 0 deletions apps/server/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const nodeExternals = require('webpack-node-externals');

/**
* `nest build --webpack` 전용 설정.
*
* 워크스페이스 패키지(@dookmark/*)는 컴파일되지 않은 raw TypeScript를
* main 필드로 내보낸다(예: @dookmark/analytics의 DASHBOARD_PERIODS 같은
* 런타임 값).
*
* tsc만 쓰면 dist에 require("@dookmark/analytics")가 남아
* 런타임에 .ts를 로드하려다 크래시한다.
*
* => webpack으로 @dookmark/* 소스만 번들에 인라인하고, 나머지 실제
* npm 의존성(@nestjs/*, prisma, socket.io, bcrypt 등)은 externalize해
* node_modules에서 그대로 require하게 한다.
* ts-loader가 실제 tsc로
* 컴파일하므로 NestJS 데코레이터 메타데이터(emitDecoratorMetadata)도 보존된다.
*
* dev(nest start)는 이 설정을 쓰지 않으므로 개발 경험은 그대로다.
*/
module.exports = (options) => ({
...options,
externals: [
nodeExternals({
// @dookmark/* 는 externalize하지 말고 번들에 포함
allowlist: [/^@dookmark\//],
}),
],
});
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading