Skip to content

Remix on AWS with SST

SST를 사용하여 AWS에 Remix 앱을 생성하고 배포하기

SST로 AWS에 Remix 배포하기

SST를 사용해 Remix 앱을 AWS에 배포하는 방법은 두 가지입니다.

  1. 서버리스
  2. 컨테이너

아래에서 두 가지 방법을 모두 사용해 간단한 앱을 만들어 보겠습니다.


예제

여러분이 참고할 수 있는 다른 Remix 예제도 몇 가지 있습니다.


서버리스

이제 Remix 앱을 만들고, 파일 업로드를 위한 S3 버킷을 추가한 다음, Remix 컴포넌트를 사용해 배포해 보겠습니다.

시작하기 전에 AWS 자격 증명을 설정했는지 확인하세요.


1. 프로젝트 생성

프로젝트를 만들어 보겠습니다.

Terminal window
npx create-remix@latest aws-remix
cd aws-remix

모든 기본 옵션을 선택합니다.


SST 초기화

이제 앱에서 SST를 초기화해 보겠습니다.

Terminal window
npx sst@latest init
npm install

기본값을 선택하고 AWS를 선택합니다. 이렇게 하면 프로젝트 루트에 sst.config.ts 파일이 생성됩니다.


개발 모드 시작

개발 모드를 시작하려면 다음 명령어를 실행하세요. 이 명령어는 SST와 여러분의 Remix 앱을 시작합니다.

Terminal window
npx sst dev

실행이 완료되면 사이드바에서 MyWeb을 클릭하고 브라우저에서 Remix 앱을 열어보세요.

2. S3 버킷 추가하기

파일 업로드를 위해 S3 버킷에 공개 access를 허용해 보겠습니다. sst.config.ts 파일을 업데이트하세요.

sst.config.ts
const bucket = new sst.aws.Bucket("MyBucket", {
access: "public"
});

이 코드를 Remix 컴포넌트 위에 추가하세요.

버킷 연결하기

이제 버킷을 Remix 앱에 연결해 보겠습니다.

sst.config.ts
new sst.aws.Remix("MyWeb", {
link: [bucket],
});

3. 업로드 폼 만들기

app/routes/_index.tsx에 업로드 폼 클라이언트를 추가합니다. Index 컴포넌트를 다음 코드로 교체하세요:

app/routes/_index.tsx
export default function Index() {
const data = useLoaderData<typeof loader>();
return (
<div className="flex h-screen items-center justify-center">
<div className="flex flex-col items-center gap-8">
<h1 className="leading text-2xl font-bold text-gray-800 dark:text-gray-100">
Welcome to Remix
</h1>
<form
className="flex flex-row gap-4"
onSubmit={async (e) => {
e.preventDefault();
const file = (e.target as HTMLFormElement).file.files?.[0]!;
const image = await fetch(data.url, {
body: file,
method: "PUT",
headers: {
"Content-Type": file.type,
"Content-Disposition": `attachment; filename="${file.name}"`,
},
});
window.location.href = image.url.split("?")[0];
}}
>
<input
name="file"
type="file"
accept="image/png, image/jpeg"
className="block w-full text-sm text-slate-500
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-violet-50 file:text-violet-700
hover:file:bg-violet-100" />
<button className="bg-violet-500 hover:bg-violet-700 text-white text-sm
font-semibold py-2 px-4 rounded-full">
Upload
</button>
</form>
</div>
</div>
);
}

4. 사전 서명된 URL 생성하기

앱이 로드될 때 파일 업로드를 위한 사전 서명된 URL을 생성하고 폼에서 사용합니다.

app/routes/_index.tsx 파일의 Index 컴포넌트 위에 다음 코드를 추가합니다.

app/routes/_index.tsx
export async function loader() {
const command = new PutObjectCommand({
Key: crypto.randomUUID(),
Bucket: Resource.MyBucket.name,
});
const url = await getSignedUrl(new S3Client({}), command);
return { url };
}

필요한 import 문을 추가합니다.

app/routes/_index.tsx
import { Resource } from "sst";
import { useLoaderData } from "@remix-run/react";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

그리고 npm 패키지를 설치합니다.

Terminal window
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

브라우저에서 로컬 Remix 앱(http://localhost:5173)으로 이동하여 이미지를 업로드해 보세요. 이미지가 업로드되고 다운로드되는 것을 확인할 수 있습니다.

SST Remix 앱 로컬


5. 앱 배포하기

이제 여러분의 앱을 AWS에 배포해 보겠습니다.

Terminal window
npx sst deploy --stage production

여기서는 어떤 스테이지 이름을 사용해도 되지만, 프로덕션을 위해 새로운 스테이지를 만드는 것이 좋습니다.

축하합니다! 이제 여러분의 사이트가 라이브 상태가 되었습니다!


컨테이너

이번에는 Remix 앱을 만들고, 파일 업로드를 위한 S3 버킷을 추가한 후, Cluster 컴포넌트를 사용해 컨테이너에 배포해 보겠습니다.

시작하기 전에 AWS 자격 증명을 설정해야 합니다.


1. 프로젝트 생성하기

먼저 앱을 만들어 보겠습니다.

Terminal window
npx create-remix@latest aws-remix-container
cd aws-remix-container

모든 기본 옵션을 선택합니다.


Init SST_fhQu7Z5f9DSet4jkhu9f9f

이제 앱에서 SST를 초기화해 보겠습니다.

Terminal window
npx sst@latest init

기본값을 선택하고 AWS를 선택합니다. 이렇게 하면 프로젝트 루트에 sst.config.ts 파일이 생성됩니다.


2. 서비스 추가하기

컨테이너에 Remix 앱을 배포하기 위해 AWS FargateAmazon ECS를 사용합니다. sst.config.ts 파일의 run 함수를 다음과 같이 수정합니다.

sst.config.ts
async run() {
const vpc = new sst.aws.Vpc("MyVpc");
const cluster = new sst.aws.Cluster("MyCluster", { vpc });
cluster.addService("MyService", {
loadBalancer: {
ports: [{ listen: "80/http", forward: "3000/http" }],
},
dev: {
command: "npm run dev",
},
});
}

이 코드는 VPC와 Fargate 서비스가 포함된 ECS 클러스터를 생성합니다.

dev.command는 SST에게 개발 모드에서 Remix 앱을 로컬에서 실행하도록 지시합니다.

개발 모드 시작

개발 모드를 시작하려면 다음 명령어를 실행하세요. 이 명령어는 SST와 여러분의 Remix 앱을 시작합니다.

Terminal window
npx sst dev

실행이 완료되면 사이드바에서 MyService를 클릭하고 브라우저에서 Remix 앱을 열어보세요.

3. S3 버킷 추가하기

파일 업로드를 위해 S3 버킷에 공개 access를 허용해 보겠습니다. sst.config.ts 파일을 업데이트하세요.

sst.config.ts
const bucket = new sst.aws.Bucket("MyBucket", {
access: "public"
});

이 코드를 Vpc 컴포넌트 아래에 추가하세요.


이제 버킷을 컨테이너에 연결합니다.

sst.config.ts
cluster.addService("MyService", {
// ...
link: [bucket],
});

이렇게 하면 Remix 앱에서 버킷을 참조할 수 있습니다.

4. 업로드 폼 만들기

app/routes/_index.tsx에 업로드 폼 클라이언트를 추가합니다. Index 컴포넌트를 다음 코드로 교체하세요:

app/routes/_index.tsx
export default function Index() {
const data = useLoaderData<typeof loader>();
return (
<div className="flex h-screen items-center justify-center">
<div className="flex flex-col items-center gap-8">
<h1 className="leading text-2xl font-bold text-gray-800 dark:text-gray-100">
Welcome to Remix
</h1>
<form
className="flex flex-row gap-4"
onSubmit={async (e) => {
e.preventDefault();
const file = (e.target as HTMLFormElement).file.files?.[0]!;
const image = await fetch(data.url, {
body: file,
method: "PUT",
headers: {
"Content-Type": file.type,
"Content-Disposition": `attachment; filename="${file.name}"`,
},
});
window.location.href = image.url.split("?")[0];
}}
>
<input
name="file"
type="file"
accept="image/png, image/jpeg"
className="block w-full text-sm text-slate-500
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-violet-50 file:text-violet-700
hover:file:bg-violet-100" />
<button className="bg-violet-500 hover:bg-violet-700 text-white text-sm
font-semibold py-2 px-4 rounded-full">
Upload
</button>
</form>
</div>
</div>
);
}

5. 사전 서명된 URL 생성하기

앱이 로드될 때 파일 업로드를 위한 사전 서명된 URL을 생성하고 폼에서 사용합니다.

app/routes/_index.tsx 파일의 Index 컴포넌트 위에 다음 코드를 추가합니다.

app/routes/_index.tsx
export async function loader() {
const command = new PutObjectCommand({
Key: crypto.randomUUID(),
Bucket: Resource.MyBucket.name,
});
const url = await getSignedUrl(new S3Client({}), command);
return { url };
}

필요한 import를 추가합니다.

app/routes/_index.tsx
import { Resource } from "sst";
import { useLoaderData } from "@remix-run/react";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

그리고 npm 패키지를 설치합니다.

Terminal window
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

브라우저에서 로컬 Remix 앱(http://localhost:5173)으로 이동해 이미지를 업로드해 보세요. 이미지가 업로드되고 다운로드되는 것을 확인할 수 있습니다.

SST Remix 앱 로컬


6. 앱 배포하기

앱을 배포하기 위해 Dockerfile을 추가합니다.

Dockerfile
FROM node:lts-alpine as base
ENV NODE_ENV production
# Stage 1: 모든 node_modules 설치 (개발 의존성 포함)
FROM base as deps
WORKDIR /myapp
ADD package.json ./
RUN npm install --include=dev
# Stage 2: 프로덕션 node_modules 설정
FROM base as production-deps
WORKDIR /myapp
COPY --from=deps /myapp/node_modules /myapp/node_modules
ADD package.json ./
RUN npm prune --omit=dev
# Stage 3: 앱 빌드
FROM base as build
WORKDIR /myapp
COPY --from=deps /myapp/node_modules /myapp/node_modules
ADD . .
RUN npm run build
# Stage 4: 프로덕션 이미지 빌드
FROM base
WORKDIR /myapp
COPY --from=production-deps /myapp/node_modules /myapp/node_modules
COPY --from=build /myapp/build /myapp/build
COPY --from=build /myapp/public /myapp/public
ADD . .
CMD ["npm", "start"]

이렇게 하면 Remix 앱이 Docker 이미지로 빌드됩니다.

또한 루트 디렉토리에 .dockerignore 파일을 추가합니다.

.dockerignore
node_modules
.cache
build
public/build

이제 Docker 이미지를 빌드하고 배포하려면 다음 명령어를 실행합니다:

Terminal window
npx sst deploy --stage production

여기서는 어떤 스테이지 이름을 사용해도 되지만, 프로덕션용으로 새로운 스테이지를 만드는 것이 좋습니다.

축하합니다! 이제 앱이 배포되었습니다!


콘솔 연결하기

다음 단계로 SST 콘솔을 설정하여 앱을 _git push로 배포_하고 로그를 확인할 수 있습니다.

SST 콘솔 자동 배포

무료 계정을 생성하고 AWS 계정에 연결할 수 있습니다.