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

Lab 02: 한 덩어리 앱 (1-Tier)

이 Lab의 흐름

Step 1
앱 실행
떠 있는데 안 열린다
Step 2
미션 ① 8080 열기
Kiro가 보안그룹을 고친다
Step 3
코드 들여다보기
화면과 API가 한 앱에 있다
Step 4
어디서 멈추는지 확인
재시작하면 데이터가 사라진다

여기부터는 EC2에서 진행합니다. 브라우저로 :9080 code-server에 접속한 상태여야 합니다.

학습 목표

여기서는 AI를 배포 도구로 씁니다. 콘솔을 클릭해 방화벽을 찾아 헤매는 대신, 말로 시켜서 앱을 밖에서 닿게 만듭니다.

  • 화면·API·데이터가 한 코드에 있는 구조를 직접 실행해봅니다
  • 코드가 돌아가는 것밖에서 닿는 것이 다른 문제임을 확인합니다
  • 액세스 키 없이 IAM Role로 AWS를 조작합니다
  • 이 구조로 어디까지 갈 수 있고 어디서 멈추는지 확인합니다
Step 1: 앱 실행하기

2.single-app화면·API·데이터가 한 코드에 있는 Next.js 앱입니다. AI에게 그냥 만들어달라고 했을 때 나오는 첫 앱의 전형적인 모습입니다.

cd /home/ec2-user/kiro-on-cloud/2.single-app
npm install
npm run dev

0.0.0.0:8080 에서 화면과 API를 한 프로세스가 서빙합니다. 이제 브라우저에서 접속해봅니다.

http://내-EC2-퍼블릭-IP:8080

안 열립니다. 서버는 분명히 떠 있는데 보안그룹이 막고 있습니다.

보안그룹 인바운드에 9080만 열려 있어 브라우저의 8080 요청이 막히고, 아웃바운드는 전부 허용되어 EC2에서 나가는 트래픽은 통과하는 구조

지금 열려 있는 인바운드는 9080 하나뿐입니다. 그 포트로 code-server에 들어와 작업하고 있으면서, 정작 8080 은 규칙이 없어 막힙니다. 나가는 방향(아웃바운드)은 전부 열려 있어서 npm install 은 잘 되는데도 그렇습니다. 들어오는 문과 나가는 문은 따로입니다.

🎯 체크포인트
  • 터미널에서 서버가 떴다는 로그를 확인했다
  • 브라우저에서는 열리지 않는 것을 확인했다
Step 2: 미션 ① — 8080 포트 열기

Kiro에게 아래를 그대로 붙여넣습니다. 콘솔에 들어가 클릭하지 않고, 말로 시켜서 인프라를 바꿔봅니다.

이 EC2에 붙은 보안그룹에 TCP 8080 인바운드를 열어줘.

액세스 키를 따로 알려주지 않았는데도 Kiro가 AWS를 조작합니다. 이 EC2에 IAM Role이 붙어 있어서 임시 자격증명을 자동으로 받아 쓰기 때문입니다. 키를 만들어 파일에 적으면 그 파일이 유출되는 순간 계정 전체가 열리지만, 이 방식은 유출될 키 자체가 없습니다.

작업이 끝나면 다시 접속합니다. 명언 받기 / 추가가 동작하면 성공입니다.

EC2 퍼블릭 IP의 8080 포트로 열린 명언집 화면. 다른 명언 보기 버튼과 명언 추가 입력칸이 있다

주소창에 도메인 없이 44.201.3.52:8080 같은 IP가 그대로 보입니다. 내 EC2에서 도는 앱을 밖에서 연 것입니다. 다른 명언 보기 를 누르고, 문장을 하나 추가해봅니다.

보안그룹 인바운드에 8080 규칙이 추가되어 브라우저의 8080 요청이 EC2의 Next.js까지 그대로 도달하는 구조

바뀐 것은 인바운드 규칙 한 줄뿐입니다. 앱은 아까와 똑같이 0.0.0.0:8080 에 떠 있었고, 코드는 한 글자도 고치지 않았습니다.

코드가 돌아가는 것과 밖에서 닿는 것은 다른 문제입니다. 앱을 아무리 잘 만들어도 인프라가 막혀 있으면 아무도 못 씁니다. 개발자도 클라우드를 알아야 하는 이유가 여기 있습니다.

포트 개방이 실패했다면

UnauthorizedOperation 이 나오면 내 것이 아닌 보안그룹을 고치려 한 것입니다. 인스턴스에 붙은 보안그룹이 kiro-handson-내사용자명 인지 확인합니다. 사전 준비에서 새 보안그룹을 만들었다면 여기서 막힙니다.

🎯 체크포인트
  • Kiro가 8080 인바운드를 추가했다
  • 브라우저에서 앱이 열리고 명언 추가가 동작한다
정적 배포와 동적 배포

Lab 01에서도 무언가를 인터넷에 올렸습니다. 게임 HTML 파일 하나를 공유 사이트에 올려 주소를 받았습니다. 방금 한 일도 인터넷에 올린 것입니다. 그런데 둘은 전혀 다른 종류의 배포입니다.

정적 배포동적 배포
올리는 것완성된 파일 (HTML·CSS·JS)실행할 코드
서버가 하는 일파일을 그대로 내어준다프로그램을 계속 돌린다
누가 언제 만드나올리기 전에 이미 다 만들어져 있다요청이 올 때마다 그 자리에서 만든다
꺼지면파일은 그대로 있다아무것도 안 된다
오늘Lab 01 게임 · Lab 03 화면Lab 02 이 앱

Lab 01의 게임은 정적이었습니다. 파일을 올려두면 끝이고, 누가 열든 같은 파일이 그대로 갑니다. 올린 쪽에서 돌아가고 있는 프로그램은 없습니다.

방금 띄운 명언집은 동적입니다. npm run dev 로 띄운 프로세스가 계속 돌고 있고, 요청이 올 때마다 그 프로세스가 응답을 만들어냅니다. 터미널에서 Ctrl+C 를 누르면 그 즉시 사이트가 죽습니다. 파일이 사라져서가 아니라 일할 사람이 없어져서입니다.

이 차이가 오늘 남은 실습을 가릅니다. 3-Tier에서 화면은 정적으로, 서버는 동적으로 배포합니다. 하나로 묶여 있던 것을 성격에 맞게 갈라놓는 것이 계층을 나눈다는 말의 실체입니다.

정적인 것과 동적인 것 중 정답은 없습니다. 어느 쪽이 낫다가 아니라 무엇을 어느 쪽에 둘 것인가의 문제입니다.

Step 3: 코드 들여다보기

동작하는 것을 봤으니, 이제 왜 이 구조가 문제인지 알기 위해 코드를 봅니다. 직접 읽지 않고 Kiro에게 물어봅니다.

`app/page.js`(화면)와 `app/api/text/route.js`(API)가 한 프로젝트에 같이 있지?
데이터는 어디에 저장돼?

Kiro의 답에서 세 가지를 확인합니다.

  1. 화면과 API가 같은 프로젝트, 같은 프로세스에 있다
  2. 데이터는 app/api/quotes-store.js메모리 배열에 있다
  3. 그래서 화면이 API를 부를 때 /api/text 라는 상대경로만으로 충분하다

세 번째가 나중에 크게 달라집니다. 계층을 나누면 상대경로가 더 이상 통하지 않습니다.

Step 4: 어디까지 되고 어디서 멈추는지 확인

여기까지 왔다면 이미 성공했습니다. 인터넷에 주소가 생겼고, 남이 열 수 있습니다. 이제 이대로 계속 써도 되는지를 확인할 차례입니다. 두 가지를 직접 눈으로 봅니다.

① 화면과 서버가 한 몸이다

app/page.js는 화면, app/api/text/route.js는 서버 로직입니다. 둘이 한 앱, 한 서버에 묶여 있습니다. 빠르게 만들기에는 이만한 구조가 없습니다. 대신 서버 하나에 모든 게 걸려 있어서, 그 서버를 건드릴 때마다 화면까지 함께 흔들립니다.

② 데이터가 서버 안에 있다

직접 해봅니다.

  1. 명언을 하나 추가합니다
  2. 새로고침합니다 — 남아 있습니다 (서버 메모리에 있으니까)
  3. 터미널에서 Ctrl+C 를 눌러 서버를 끄고, 다시 npm run dev 합니다
  4. 명언 받기를 눌러봅니다 — 방금 추가한 게 사라졌습니다

데이터가 서버 메모리에만 있기 때문입니다. 아직 DB가 없습니다.

여기까지가 한 덩어리로 갈 수 있는 거리입니다. 오래 버티게 만들려면 데이터와 화면을 서버 밖으로 꺼내야 합니다. 그게 다음에 할 일입니다.

🎯 체크포인트
  • 명언을 추가하고 새로고침해서 남아 있는 것을 확인했다
  • 서버를 재시작하니 추가한 명언이 사라지는 것을 확인했다

다음 단계

배포는 이미 해냈습니다. 이제 같은 앱을 화면(S3) · 로직(EC2 FastAPI) · 데이터(MySQL) 로 나눠 오래 버티는 구조로 다시 세웁니다. 화면부터 떼어냅니다. 정적으로 올릴 수 있는 부분이라 가장 먼저 자기 자리를 찾아갈 수 있습니다.

지금
Lab 02 — 한 덩어리로 배포했다
재시작하면 데이터가 사라진다
다음
Lab 03 — 프레젠테이션 계층
화면만 떼어 S3에 정적 배포합니다