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

Lab 04: 데이터·애플리케이션 계층과 완성

이 Lab의 흐름

S3의 화면이 부르고 있는 그 주소에, 이제 서버를 세웁니다.

Step 1
데이터 계층 — MySQL 설치
데이터가 살아남을 곳부터
Step 2
애플리케이션 계층 — FastAPI
TODO 3개를 Kiro가 채운다
Step 3
서버 실행과 8000 열기
화면이 그제야 살아난다
Step 4
완성 확인
지속성 · 독립성 · 안전성

서버 코드는 TODO 로 비어 있습니다. 여러분이 채우는 게 아니라 Kiro에게 시킵니다.

학습 목표

여기서는 AI를 개발과 배포에 함께 씁니다. DB를 설치하는 일은 시켜서 하고, 비어 있는 서버 코드는 시켜서 채웁니다. 두 가지가 한 Lab 안에서 이어집니다.

  • 데이터를 프로세스 밖(DB)으로 꺼내면 무엇이 달라지는지 확인합니다
  • 손이 더 가는 대신 데이터 영속성을 얻는다는 것을 겪습니다
  • DB 접속 정보를 서버에만 두는 이유를 이해합니다
  • 3-Tier가 실제로 지속성·독립성·안전성을 주는지 직접 검증합니다

마지막 검증이 오늘의 결론입니다. 좋은 구조라는 말을 믿는 게 아니라, 서버를 껐다 켜고 개발자도구를 열어 내 눈으로 확인합니다.

Step 1: 데이터 계층 — MySQL 설치

Kiro에게 아래를 붙여넣습니다.

이 EC2(Amazon Linux 2023)에 MariaDB를 설치·실행하고,
`3.structured-quote-app/server/schema.sql`을 root로 실행해서 DB·테이블·계정을 만들어줘.

Kiro가 실행하는 명령은 대략 이렇습니다. 직접 칠 필요는 없지만, 무슨 일이 벌어지는지는 알고 넘어갑니다.

sudo dnf install -y mariadb105-server
sudo systemctl enable --now mariadb
sudo mysql < /home/ec2-user/kiro-on-cloud/3.structured-quote-app/server/schema.sql

schema.sql 은 DB와 테이블을 만들고, 앱이 쓸 전용 계정까지 만듭니다. root 계정을 앱에 주지 않는 것이 기본입니다.

확인

초기 명언 2개가 들어갔는지 봅니다.

mysql -u appuser -papppassword quotes_db -e "SELECT * FROM quotes;"
🎯 체크포인트
  • MariaDB가 실행 중이다
  • quotes 테이블에 명언 2개가 보인다
Step 2: 애플리케이션 계층 — FastAPI TODO 채우기

3.structured-quote-app/server/main.py 에는 구현이 비어 있는 TODO(KIRO) 가 세 개 있습니다. Kiro에게 채우게 합니다.

`3.structured-quote-app/server/main.py`의 `TODO(KIRO)` 세 개를 주석 설명대로 구현해줘.
MySQL의 `quotes` 테이블을 쓰고, 응답 형식은 1-Tier와 똑같이 유지해.

마지막 문장이 핵심입니다. 응답 형식을 그대로 유지해야 Lab 03에서 이미 S3에 올린 화면이 수정 없이 붙습니다. 계층을 나눌 때 계층 사이의 약속(API 형식)을 지키는 것이 곧 호환입니다.

세 번째 TODO가 오늘의 포인트

/api/texts(저장된 명언 목록)를 보세요. 1-Tier에서는 메모리 배열 하나를 읽으면 끝이었지만, 서버를 껐다 켜면 그대로 사라졌습니다. 계층을 나누면 API 엔드포인트와 SQL 쿼리를 따로 만들어야 합니다. 그 대신 값이 DB에 남습니다. 서버를 재시작해도, 앱을 새로 띄워도 그대로 있습니다. 데이터 영속성을 갖게 되는 지점입니다.

화면은 이미 목록 UI를 갖고 있습니다. 서버만 채워지면 저장된 명언 N 버튼의 숫자가 살아납니다. 나눈다는 건 이렇게 없던 일이 생기는 것이기도 합니다.

🎯 체크포인트
  • main.py의 TODO 3개가 모두 구현됐다
  • 응답 형식이 1-Tier와 같은지 코드에서 확인했다
Step 3: 서버 실행하고 8000 포트 열기
cd /home/ec2-user/kiro-on-cloud/3.structured-quote-app/server
cp .env.example .env
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000

.env 의 값은 수정하지 않습니다. schema.sql 이 만든 계정과 이미 일치합니다.

두 가지를 기억합니다

--host 0.0.0.0 을 빼면 기본값이 127.0.0.1 이라 EC2 안에서만 닿습니다. S3에 올린 화면이 이 서버를 못 부릅니다.

.env 의 DB 비밀번호는 서버에만 둡니다. Step 4에서 이 비밀번호가 브라우저로 새지 않는 것을 직접 확인합니다.

확인

curl http://localhost:8000/api/text
Connection refused 가 나온다면

Can't connect to MySQL server ... Connection refused 는 인증 문제가 아니라 3306 포트에서 아무도 듣고 있지 않다는 뜻입니다. MariaDB가 떠 있는지 확인합니다.

sudo systemctl status mariadb
sudo systemctl start mariadb

미션 ③ — 8000 포트 열기

curl 은 되는데 S3 화면에서는 아직 안 됩니다. 지금 응답을 받은 건 EC2 자기 자신이고, 밖에서 오는 요청은 여전히 막혀 있습니다. Lab 02에서 8080을 열었던 것과 똑같은 상황입니다.

이 EC2의 보안그룹에 TCP 8000 인바운드를 추가해줘.

이제 S3 화면을 새로고침합니다

Lab 03에서 받은 S3 URL을 열고 다른 명언 보기 를 눌러봅니다. 동작합니다.

S3는 손대지 않았습니다. 다시 빌드하지도, 다시 올리지도 않았습니다. 화면이 부르던 주소에 서버가 나타나고 문이 열렸을 뿐입니다. 계층 사이의 약속이 주소와 응답 형식 두 가지뿐이라는 것이 여기서 드러납니다.

🎯 체크포인트
  • curl 로 명언이 JSON으로 돌아왔다
  • --host 0.0.0.0 으로 실행했다
  • 8000 포트 인바운드가 추가됐다
  • S3 화면에서 명언 받기와 추가가 동작한다
Step 4: 완성 확인 — 지속성 · 독립성 · 안전성

세 가지를 직접 해봅니다. 읽고 넘어가지 말고 손으로 합니다.

① 지속성

명언을 추가하고, FastAPI 서버를 재시작합니다(Ctrl+C 후 다시 uvicorn). 그리고 명언 받기를 눌러봅니다.

살아 있습니다. 데이터가 MySQL에 있기 때문입니다. Lab 02에서 사라졌던 그 데이터입니다.

② 독립성

서버(EC2)가 잠깐 죽어도 화면(S3)은 그대로 떠 있습니다. 계층이 분리됐기 때문입니다. 1-Tier였다면 화면까지 같이 죽었습니다.

Lab 03에서 이미 겪었습니다. 서버가 없어도 화면은 떴습니다. 그때는 미완성으로 보였지만, 사실 그게 독립성입니다.

③ 안전성

F12로 개발자도구를 열고 화면 코드를 다 뒤져봅니다. DB 비밀번호가 없습니다. .envDB_PASSWORD 는 서버(EC2)에만 있습니다.

화면 코드로 나가는 건 next.config.jsenv 에 적은 것뿐입니다. 번들에 담기는 건 전부 공개되기 때문에 프레임워크가 일부러 막아둔 것입니다.

위험한 일(DB 접속)은 닫힌 공간(서버)이 대신 합니다. 그래서 계층을 나눕니다.

🎯 체크포인트
  • 서버를 재시작해도 추가한 명언이 남아 있다
  • 서버가 죽어도 화면은 열린다
  • 개발자도구에서 DB 비밀번호를 찾을 수 없다
막혔을 때
증상원인해결
브라우저에서 앱이 안 열림보안그룹 포트가 안 열림Kiro에게 해당 포트 인바운드 추가 요청
포트 개방 실패 UnauthorizedOperation내 것이 아닌 보안그룹인스턴스에 kiro-handson-내사용자명 이 붙어 있는지 확인
화면은 뜨는데 명언이 안 나옴서버가 안 떠 있거나 SERVER_URL 없이 빌드함 (가장 흔함)F12 Network에서 요청 주소를 확인. S3 주소로 가면 다시 빌드 후 재업로드
S3 버킷 생성 실패이름이 내 사용자명으로 시작하지 않음aws sts get-caller-identity 로 이름 확인 후 다시
Can't connect to MySQL ... Connection refusedMariaDB가 안 떠 있음sudo systemctl status mariadbsudo systemctl start mariadb
서버는 뜨는데 S3 화면에서만 실패--host 0.0.0.0 을 빼고 실행함uvicorn을 --host 0.0.0.0 으로 다시 실행
EC2 재시작 후 화면이 먹통퍼블릭 IP가 바뀜새 IP로 재빌드 후 재업로드

어떤 에러든 Kiro에게 그대로 붙여넣는 것이 가장 빠릅니다. 에러 메시지 전체를 복사해서 “이 에러가 왜 나는지 알려주고 고쳐줘”라고 하면 됩니다. 요약하지 말고 통째로 붙여넣는 편이 낫습니다.

오늘 배운 것
  • AI에게 시켜서 앱을 배포했습니다. 서버를 빌리고 포트를 여는 일에 더는 겁먹지 않습니다
  • 한 덩어리(1-Tier) 는 빠르게 시작하기 좋고, 세 계층(3-Tier) 은 오래 버티기 좋습니다. 언제 무엇을 고를지 감이 생겼습니다
  • 정적 배포와 동적 배포를 둘 다 손으로 했습니다. 올려두는 것과 띄워두는 것은 다른 일입니다
  • 나누면 각자 독립적으로 살아남고, 비밀은 서버 안에 남습니다
  • 나누는 순간 서로의 주소를 알아야 하고, 포트를 열어야 합니다 — 이게 클라우드 작업의 실체입니다
  • AI에게 시키려면 무엇을 원하는지 말할 수 있어야 합니다. 그리고 결과는 내가 확인해야 합니다
  • EC2 안에 비밀 키를 하나도 두지 않았습니다. IAM Role이 클라우드에서 권한을 다루는 정석입니다

한 걸음 더 — 실무는 어떻게 하나

오늘 DB는 EC2와 같은 서버 안에 있었습니다. 서버가 터지면 DB도 같이 갑니다.

실무에서는 DB를 별도 서버(AWS RDS) 로 분리합니다. 그러면 서버가 죽어도 데이터는 무사하고, 백업과 확장도 따로 할 수 있습니다. 데이터 계층을 물리적으로도 분리하는 것이 진짜 3-Tier입니다.

아키텍처를 더 깊이 파고드는 건 여기서부터입니다. 다만 오늘 확인한 것이 먼저입니다. 서버를 빌리고, 포트를 열고, 인터넷에 주소를 만드는 일을 말로 시켜서 끝까지 해냈다는 사실입니다.

부록: 저장소 구조
kiro-on-cloud/
├── 1.basic-game/                    워밍업용 단일 HTML 게임 4개

├── 2.single-app/                    ① 한 덩어리 앱 (Next.js)
│   └── app/
│       ├── page.js                     화면
│       ├── api/quotes-store.js         메모리 배열 (재시작하면 증발)
│       ├── api/text/route.js           명언 1개 조회 · 추가
│       └── api/texts/route.js          저장된 명언 목록

└── 3.structured-quote-app/          ② 세 계층으로 나눈 앱
    ├── client/                         프레젠테이션 — S3에 올릴 화면
    │   ├── app/page.js                    API 라우트 없음. 서버에만 요청
    │   ├── next.config.js                 output:"export" + SERVER_URL 주입
    │   └── .env.example                   SERVER_URL 설정용
    └── server/                         애플리케이션 — EC2에서 도는 API
        ├── main.py                        FastAPI 서버 (TODO 3개 ← Kiro가 채움)
        ├── schema.sql                     DB·테이블·계정 생성
        ├── requirements.txt
        └── .env.example                   DB 접속 정보 (수정 불필요)

직접 타이핑하는 코드는 없습니다. server/main.py 의 TODO 3개조차 Kiro가 채웁니다. 우리가 하는 일은 무엇을 원하는지 정확히 말하고, 결과를 확인하는 것입니다.