서버리스와 함께 React.js 앱 만들기
React.js 앱을 서버리스로 만드는 방법
이 예제에서는 React.js와 서버리스 API를 사용해 간단한 클릭 카운터 앱을 만드는 방법을 살펴보겠습니다. SST와 SST의 StaticSite
구성을 사용해 앱을 AWS에 배포할 것입니다.
요구사항
- Node.js 16 이상
- TypeScript 사용
- AWS 계정과 로컬에 구성된 AWS CLI
SST 앱 만들기
먼저 SST 앱을 만들어 보겠습니다.
$ npx create-sst@latest --template=base/example react-app
$ cd react-app
$ npm install
기본적으로 앱은 AWS us-east-1
리전에 배포됩니다. 이 설정은 프로젝트 루트의 sst.config.ts
파일에서 변경할 수 있습니다.
import { SSTConfig } from "sst";
export default {
config(_input) {
return {
name: "react-app",
region: "us-east-1",
};
},
} satisfies SSTConfig;
프로젝트 구조
SST 앱은 몇 가지 부분으로 구성됩니다.
-
stacks/
— 앱 인프라서버리스 앱의 인프라를 정의하는 코드는 프로젝트의
stacks/
디렉토리에 위치합니다. SST는 AWS CDK를 사용하여 인프라를 생성합니다. -
packages/functions/
— 앱 코드API가 호출될 때 실행되는 코드는 프로젝트의
packages/functions/
디렉토리에 위치합니다. -
packages/frontend/
— React 앱프론트엔드 React.js 앱의 코드입니다.
인프라 구축하기
우리 앱은 간단한 API와 React.js 앱으로 구성됩니다. API는 데이터베이스와 통신하여 클릭 횟수를 저장합니다. 데이터베이스부터 만들어 보겠습니다.
테이블 추가하기
Amazon DynamoDB를 사용할 예정입니다. 이는 신뢰할 수 있고 고성능의 NoSQL 데이터베이스로, 진정한 서버리스 데이터베이스로 구성할 수 있습니다. 즉, 자동으로 확장 및 축소되며, 사용하지 않을 때는 요금이 부과되지 않습니다.
stacks/ExampleStack.ts
를 다음 코드로 교체하세요.
import { Api, StaticSite, StackContext, Table } from "sst/constructs";
export function ExampleStack({ stack }: StackContext) {
// 테이블 생성
const table = new Table(stack, "Counter", {
fields: {
counter: "string",
},
primaryIndex: { partitionKey: "counter" },
});
}
이 코드는 SST의 Table
구성을 사용하여 서버리스 DynamoDB 테이블을 생성합니다. counter
라는 기본 키를 가지고 있습니다. 우리의 테이블은 다음과 같이 생겼을 것입니다:
counter | tally |
---|---|
clicks | 123 |
API 생성하기
이제 API를 추가해 보겠습니다.
stacks/ExampleStack.ts
파일에서 Table
정의 아래에 다음 코드를 추가하세요.
// HTTP API 생성
const api = new Api(stack, "Api", {
defaults: {
function: {
// 테이블 이름을 API에 바인딩
bind: [table],
},
},
routes: {
"POST /": "packages/functions/src/lambda.handler",
},
});
// 출력에 URL 표시
stack.addOutputs({
ApiEndpoint: api.url,
});
여기서는 SST의 Api
구문을 사용해 API를 생성합니다. 이 API는 단일 엔드포인트(루트)를 가지고 있습니다. 이 엔드포인트에 POST
요청을 보내면 packages/functions/src/lambda.ts
파일에 있는 handler
라는 Lambda 함수가 호출됩니다.
또한, 방금 생성한 테이블을 API에 바인딩합니다. 이를 통해 API가 테이블에 접근(읽기 및 쓰기)할 수 있게 됩니다.
React 앱 설정하기
React.js 앱을 AWS에 배포하기 위해 SST의 StaticSite
구성을 사용할 것입니다.
stacks/ExampleStack.ts
에서 다음 부분을 교체하세요:
// API 엔드포인트를 출력에 표시
stack.addOutputs({
ApiEndpoint: api.url,
});
다음으로 교체하세요:
// React 앱 배포
const site = new StaticSite(stack, "ReactSite", {
path: "packages/frontend",
buildCommand: "npm run build",
buildOutput: "build",
environment: {
REACT_APP_API_URL: api.url,
},
});
// URL을 출력에 표시
stack.addOutputs({
SiteUrl: site.url,
ApiEndpoint: api.url,
});
이 구성은 React.js 앱이 위치한 디렉토리를 가리킵니다. 아직 앱을 만들지 않았지만, 지금은 packages/frontend
디렉토리를 가리키도록 설정합니다.
또한 API의 엔드포인트를 사용하여 빌드 시점 React 환경 변수 REACT_APP_API_URL
를 설정합니다. StaticSite
를 사용하면 프론트엔드에서 하드 코딩하지 않고도 백엔드에서 자동으로 환경 변수를 설정할 수 있습니다. 이에 대한 자세한 내용은 React 앱에서 서버리스 환경 변수 설정하기 챕터에서 확인할 수 있습니다.
선택적으로 커스텀 도메인을 설정할 수도 있습니다.
// React 앱 배포
const site = new StaticSite(stack, "ReactSite", {
// ...
customDomain: "www.my-react-app.com",
});
하지만 지금은 이 부분을 건너뛰겠습니다.
테이블에서 데이터 읽기
우리 API는 Lambda 함수로 동작합니다. 이 함수에서 DynamoDB 테이블에서 데이터를 읽어올 것입니다.
packages/functions/src/lambda.ts
파일을 다음 코드로 교체하세요.
import { DynamoDB } from "aws-sdk";
import { Table } from "sst/node/table";
const dynamoDb = new DynamoDB.DocumentClient();
export async function handler() {
const getParams = {
// 환경 변수에서 테이블 이름을 가져옵니다.
TableName: Table.Counter.tableName,
// "clicks"라는 카운터가 있는 행을 가져옵니다.
Key: {
counter: "clicks",
},
};
const results = await dynamoDb.get(getParams).promise();
// 행이 존재하면 "tally" 컬럼의 값을 가져옵니다.
let count = results.Item ? results.Item.tally : 0;
return {
statusCode: 200,
body: count,
};
}
DynamoDB 테이블에 get
요청을 보내고, counter
컬럼의 값이 clicks
인 행의 값을 가져옵니다. 아직 이 컬럼에 값을 쓰지 않았기 때문에, 0
을 반환합니다.
packages/functions/
폴더에서 aws-sdk
패키지를 설치하세요.
$ npm install aws-sdk
그리고 지금까지 작업한 내용을 테스트해 보세요.
개발 환경 시작하기
SST는 Live Lambda Development 환경을 제공합니다. 이를 통해 여러분은 서버리스 앱을 실시간으로 작업할 수 있습니다.
$ npm run dev
이 명령어를 처음 실행하면 앱과 Live Lambda Development 환경을 지원하기 위한 디버그 스택을 배포하는 데 몇 분 정도 걸립니다.
===============
앱 배포 중
===============
SST 앱 준비 중
소스 코드 변환 중
소스 코드 린팅 중
스택 배포 중
dev-react-app-ExampleStack: 배포 중...
✅ dev-react-app-ExampleStack
스택 dev-react-app-ExampleStack
상태: 배포 완료
출력:
ApiEndpoint: https://51q98mf39e.execute-api.us-east-1.amazonaws.com
SiteUrl: http://localhost:5173
ApiEndpoint
는 방금 생성한 API입니다. SiteUrl
은 React 앱이 로컬에서 실행될 주소입니다.
SST Console을 사용해 엔드포인트를 테스트해 보겠습니다. SST Console은 SST 앱을 관리할 수 있는 웹 기반 대시보드입니다. 문서에서 자세히 알아보세요.
API 탭으로 이동한 후 Send 버튼을 클릭해 POST
요청을 보냅니다.
API 탐색기를 사용하면 Api
구조체의 모든 라우트에 HTTP 요청을 보낼 수 있습니다. 헤더, 쿼리 파라미터, 요청 본문을 설정하고 응답과 함께 함수 로그를 확인할 수 있습니다.
응답 본문에 0
이 표시되는 것을 확인할 수 있습니다.
React 앱 설정하기
이제 방금 만든 API를 사용할 준비가 되었습니다. Create React App을 사용해 React.js 앱을 설정해 보겠습니다.
프로젝트 루트에서 다음 명령어를 실행하세요.
$ npx create-react-app packages/frontend --use-npm
$ cd frontend
이 명령어는 packages/frontend/
디렉토리에 React 앱을 설정합니다. 이전 가이드에서 StaticSite
구성을 이 경로로 지정했던 것을 기억하세요.
Create React App은 Jest를 사용하는 리포지토리 내부에 설치될 경우 경고를 발생시킵니다. 이를 비활성화하려면 환경 변수를 설정해야 합니다.
frontend/.env
파일에 다음을 추가하세요.
SKIP_PREFLIGHT_CHECK=true
또한 SST 앱의 환경 변수를 불러와야 합니다. 이를 위해 sst bind
커맨드를 사용할 것입니다.
frontend/package.json
의 start
스크립트를 다음으로 교체하세요.
"start": "react-scripts start",
다음으로 교체합니다:
"start": "sst bind react-scripts start",
이제 React 개발 환경을 시작해 보겠습니다.
packages/frontend/
디렉토리에서 다음을 실행하세요.
$ npm run start
이 명령어는 브라우저에서 React.js 앱을 열어줄 것입니다.
클릭 버튼 추가하기
이제 앱의 UI를 추가하고 서버리스 API와 연결할 준비가 되었습니다.
packages/frontend/src/App.js
를 다음 코드로 교체하세요.
import { useState } from "react";
import "./App.css";
export default function App() {
const [count, setCount] = useState(null);
function onClick() {
fetch(process.env.REACT_APP_API_URL, {
method: "POST",
})
.then((response) => response.text())
.then(setCount);
}
return (
<div className="App">
{count && <p>You clicked me {count} times.</p>}
<button onClick={onClick}>Click Me!</button>
</div>
);
}
여기서는 클릭 시 API에 요청을 보내는 간단한 버튼을 추가했습니다. API 엔드포인트는 환경 변수 process.env.REACT_APP_API_URL
에서 가져옵니다.
API의 응답은 앱의 상태에 저장됩니다. 이를 사용하여 버튼이 클릭된 횟수를 표시합니다.
이제 스타일을 추가해 보겠습니다.
packages/frontend/src/App.css
를 다음 코드로 교체하세요.
body,
html {
height: 100%;
display: grid;
}
#root {
margin: auto;
}
.App {
text-align: center;
}
p {
margin-top: 0;
font-size: 20px;
}
button {
font-size: 48px;
}
이제 브라우저로 이동하면 React 앱이 다음과 같이 보일 것입니다.
물론 버튼을 여러 번 클릭해도 카운트가 변경되지 않습니다. 이는 API에서 카운트를 업데이트하지 않기 때문입니다. 다음 단계에서 이를 처리할 것입니다.
변경 사항 적용하기
클릭 횟수로 테이블을 업데이트해 보겠습니다.
packages/functions/src/lambda.ts
파일의 return
문 위에 다음 코드를 추가하세요.
const putParams = {
TableName: Table.Counter.tableName,
Key: {
counter: "clicks",
},
// "tally" 컬럼 업데이트
UpdateExpression: "SET tally = :count",
ExpressionAttributeValues: {
// 카운트 증가
":count": ++count,
},
};
await dynamoDb.update(putParams).promise();
여기서는 clicks
행의 tally
컬럼을 증가된 카운트로 업데이트합니다.
이제 브라우저로 이동해 버튼을 다시 클릭하면 카운트가 증가하는 것을 확인할 수 있습니다!
또한 SST 콘솔의 DynamoDB 탭으로 이동해 테이블의 값이 업데이트되었는지 확인해 보세요.
참고로, DynamoDB 탐색기를 사용하면 앱의 Table
구성 요소에 있는 DynamoDB 테이블을 쿼리할 수 있습니다. 테이블을 스캔하거나 특정 키를 쿼리하고, 아이템을 생성 및 편집할 수 있습니다.
프로덕션 환경에 배포하기
마지막으로 앱을 프로덕션 환경에 배포해 보겠습니다.
$ npx sst deploy --stage prod
이 명령어를 사용하면 환경을 분리할 수 있습니다. 따라서 dev
환경에서 작업할 때 사용자에게 영향을 주지 않습니다.
배포가 완료되면 다음과 같은 결과를 확인할 수 있습니다.
✅ prod-react-app-ExampleStack
Stack prod-react-app-ExampleStack
Status: deployed
Outputs:
ApiEndpoint: https://ck198mfop1.execute-api.us-east-1.amazonaws.com
SiteUrl: https://d1wuzrecqjflrh.cloudfront.net
아래 명령어를 실행하여 prod 스테이지에서 SST 콘솔을 열고 프로덕션 엔드포인트를 테스트할 수 있습니다.
npx sst console --stage prod
API 탭으로 이동한 후 Send 버튼을 클릭하여 POST
요청을 보냅니다.
브라우저에서 SiteUrl
로 이동하면 새로운 React 앱이 동작하는 모습을 확인할 수 있습니다!
정리하기
마지막으로, 이 예제에서 생성한 리소스들을 다음 명령어로 제거할 수 있습니다.
$ npx sst remove
$ npx sst remove --stage prod
결론
이제 여러분은 React.js로 완전한 서버리스 클릭 카운터를 만들었습니다. 로컬 개발 환경에서 테스트하고 변경할 수 있으며, 프로덕션에도 배포되어 사용자와 공유할 수 있습니다. 아래 레포지토리에서 이 예제에 사용된 코드를 확인해 보세요. 궁금한 점이 있다면 댓글을 남겨 주세요!
For help and discussion
Comments on this example