NxtCloud NxtCloud Workshop / AWS KIRO로 배우는 AWS Cloud 배포
로그인

Lab 03: 프레젠테이션 계층 — 화면을 S3로

이 Lab의 흐름

같은 앱을 세 계층으로 다시 세웁니다. 화면부터 떼어냅니다.

Step 1
화면이 어떻게 달라졌는지 보기
API 라우트가 없다
Step 2
미션 ② 빌드해서 S3에 올리기
정적 배포를 직접 한다
Step 3
열어서 확인
화면은 뜨는데 명언이 안 나온다

Lab 02에서 정적 배포와 동적 배포의 차이를 봤습니다. 여기서 하는 것은 정적 배포입니다.

학습 목표

여기서는 AI를 설계 도구로 씁니다. 앞에서는 이걸 해줘였다면, 여기서는 구조를 정해놓고 그 구조대로 만들게 합니다.

  • 실행되는 앱과 올려두는 파일의 차이를 손으로 확인합니다
  • 계층을 나누면 왜 서로의 주소를 알아야 하는지 이해합니다
  • 화면만 있고 서버가 없으면 무엇이 안 되는지 직접 겪습니다

화면을 먼저 올리는 이유가 있습니다. 세 계층 중 가장 먼저 눈에 보이는 것이 화면이고, 그 화면이 혼자서는 아무것도 못 한다는 사실이 나머지 두 계층을 만들 이유가 됩니다.

Step 1: 화면이 어떻게 달라졌는지 보기

3.structured-quote-app/client 는 한 덩어리 앱에서 화면만 남은 Next.js입니다. 1-Tier와 같은 프레임워크인데 결정적으로 다른 점이 있습니다.

API 라우트가 없습니다. 데이터를 직접 갖지 않고, 서버에 /api/text 를 호출할 뿐입니다.

next.config.js 를 보면 output: "export" 가 있습니다. 이 설정 때문에 npm run build 결과가 실행 가능한 서버가 아니라 out/ 폴더 안의 정적 파일 묶음이 됩니다. 서버가 필요 없으니 S3에 그냥 올려두면 됩니다.

1-Tier의 화면3-Tier의 화면
결과물실행되는 Next.js 서버out/ 안의 HTML·JS 파일
사는 곳EC2S3
배포 방식동적 배포정적 배포
API 위치자기 안 (/api/text)남의 서버 (http://IP:8000/api/text)

Lab 01에서 게임 HTML 하나를 공유 사이트에 올렸던 것과 같은 방식입니다. 그때는 파일이 하나였고 지금은 폴더 하나라는 차이뿐입니다.

Step 2: 미션 ② — 빌드해서 S3에 올리기

이 Step은 둘로 나뉩니다. 서버 주소는 손으로 적고, 배포는 Kiro에게 시킵니다.

서버 주소는 손으로 적습니다

cd /home/ec2-user/kiro-on-cloud/3.structured-quote-app/client
cp .env.example .env

.env 를 편집기에서 열고 서버 주소를 채웁니다. 내 EC2의 퍼블릭 IP에 8000 포트입니다.

SERVER_URL=http://내-EC2-퍼블릭-IP:8000

이 한 줄이 화면과 서버를 잇는 유일한 연결입니다. 나중에 화면에 명언이 안 나오면 가장 먼저 열어볼 파일이 여기고, EC2 IP가 바뀌면 고쳐야 할 곳도 여기입니다. 어디를 봐야 하는지 알려면 한 번은 직접 적어봐야 합니다.

서버는 아직 없습니다. 주소만 먼저 정해두는 것입니다. 그 자리에 들어올 서버는 다음 Lab에서 만듭니다.

배포는 시킵니다

빌드와 업로드는 절차입니다. 정해진 순서를 정확히 반복하는 일이라 AI가 가장 잘하는 영역입니다.

`3.structured-quote-app/client`를 프로덕션 빌드해서
S3 버킷 `summer-ht-__-app`에 정적 웹사이트 호스팅으로 올려줘.
완성된 S3 웹사이트 URL을 알려줘.

버킷 이름의 summer-ht-__ 자리에는 내 IAM 사용자명을 넣습니다. 다른 이름으로는 생성이 거부됩니다.

Kiro가 실행하는 명령은 대략 이렇습니다.

npm install
npm run build             # out/ 폴더가 생긴다
aws s3 sync out/ s3://summer-ht-__-app/ --delete

내가 정할 것과 맡길 것이 갈립니다. 주소는 내 환경에 달린 값이라 내가 정하고, 빌드와 업로드는 정해진 절차라 맡깁니다. AI를 쓴다는 건 전부 넘기는 게 아니라 이 선을 긋는 일입니다.

🎯 체크포인트
  • client/.env 에 서버 주소를 직접 적었다
  • S3 웹사이트 URL을 받았다
Step 3: 열어서 확인하기

받은 S3 URL로 접속합니다.

화면은 뜹니다. 제목도, 버튼도, 입력칸도 전부 그대로입니다. EC2에서 아무 프로세스도 띄우지 않았는데 화면이 나옵니다. 정적 배포이기 때문입니다.

이제 다른 명언 보기 를 눌러봅니다. 아무것도 오지 않습니다.

F12로 개발자도구의 Network 탭을 열고 다시 눌러봅니다.

S3 웹사이트에서 다른 명언 보기를 눌렀을 때 개발자도구 Network 탭에 text 요청이 빨간 X로 실패하고, Request URL이 http://44.201.3.52:8000/api/text로 표시된 화면

빨간 X가 붙은 text 를 클릭하면 Request URL이 나옵니다. http://44.201.3.52:8000/api/text, 조금 전 .env 에 적은 그 주소입니다. 요청이 안 나간 게 아니라 나갔는데 실패한 것입니다.

주소는 정확합니다. 화면이 부르는 곳도 맞습니다. 다만 그 주소에 아직 아무것도 없습니다. 서버를 띄우지 않았고, 8000 포트도 열지 않았습니다. 둘 다 다음 Lab에서 합니다.

화면만으로는 아무것도 못 합니다. 명언을 꺼내오는 일도, 새로 저장하는 일도 서버가 대신 해줘야 합니다. 나눈다는 건 각자 자기 몫만 갖는다는 뜻입니다.

🎯 체크포인트
  • S3 URL로 화면이 열렸다
  • Network 탭에서 Request URL이 내 EC2의 :8000 인 것과, 그 요청이 실패한 것을 확인했다
핵심: 화면은 왜 서버의 주소를 알아야 하는가

오늘 워크숍에서 가장 중요한 부분입니다.

사용자가 S3에 올라간 화면을 열고, 그 화면이 SERVER_URL에 적힌 주소로 EC2 8000 포트의 서버에 요청을 보내는 흐름

사용자는 S3 화면을 엽니다. 그 화면이 다시 서버를 부릅니다. 두 번째 화살표에 이름이 붙어 있는 이유가 있습니다. 부를 곳을 누가 알려주지 않으면 화면은 움직이지 못합니다.

1-Tier에서는 주소가 필요 없었습니다. 화면과 API가 같은 앱이라 /api/text 라는 상대경로면 끝이었습니다. 브라우저는 지금 보고 있는 사이트에 그대로 요청하면 됐습니다.

S3로 떼어내는 순간 브라우저는 서버가 어디 있는지 전혀 모릅니다. S3에는 화면 파일만 있고 API가 없습니다. 그래서 빌드할 때 주소를 파일 안에 박아 넣어야 합니다.

동작 방식은 이렇습니다. app/page.jsprocess.env.SERVER_URL 을 읽고, Next.js가 .env 에 적힌 값을 빌드 시점에 문자열로 치환합니다. 빌드가 끝난 out/_next/static/ 안의 JS 파일에는 IP가 그대로 들어가 있습니다. 방금 손으로 적은 한 줄이 여기까지 옵니다.

정리하면 계층을 나눈다는 건 서로의 주소를 알아야 한다는 뜻입니다. .env 에 IP를 적은 것도, 다음 Lab에서 8000 포트를 열어야 하는 것도 전부 나눴기 때문에 생긴 일입니다.

주의: EC2를 중지했다 켜면 퍼블릭 IP가 바뀝니다. 그러면 S3 화면이 옛 IP를 부릅니다. 파일 교체로는 해결되지 않고 재빌드 후 재업로드해야 합니다. IP가 out/_next/static/ 안의 JS에 박혀 있기 때문입니다.

다음 단계

화면은 자기 자리를 찾았습니다. 부를 상대가 없을 뿐입니다.

Lab 04에서 그 상대를 만듭니다. 데이터가 살아남을 곳(MySQL)을 먼저 세우고, 그 위에 서버(FastAPI)를 올린 다음, 8000 포트를 엽니다. S3는 다시 건드리지 않습니다. 주소는 이미 박혀 있으니, 그 주소에서 서버가 응답하기 시작하면 화면이 저절로 살아납니다.

지금
Lab 03 — 화면은 S3에 올라갔다
부를 서버가 없다
다음
Lab 04 — 데이터·애플리케이션 계층
MySQL과 FastAPI를 세워 화면을 살린다