들어가며

대학 행정 AI 에이전트 '슈메이트'를 만들고 있습니다. 교내에 흩어져 있는 데이터를 모아 실제 업무와 연결하는 서비스라, 결국 데이터를 어떻게 담을 것인가가 프로젝트의 절반이었습니다.

이에 기능명세서를 쓰는 것부터 AWS RDS에 테이블을 올리기까지의 과정을 정리해봤습니다.

1. 기능명세서

데이터베이스를 만들기 전에 어떤 데이터가 필요한지부터 알아야 했습니다. 스키마는 결국 기능에서 나오기 때문입니다.

그래서 팀원들과 논의하며 기능명세서를 먼저 작성했습니다. 돌아보면 전체 과정에서 여기에 가장 많은 시간을 썼습니다. 그런데 이 단계를 대충 넘겼다면 뒤에서 몇 배로 되돌아왔을 것 같습니다.

기능이 정해지지 않으면 "이 컬럼이 필요한가?"라는 질문에 답할 수가 없습니다.

2. 데이터 리스트업

기능명세서를 훑으면서 각 기능이 필요로 하는 데이터를 뽑아냈습니다.

예를 들어 "동아리를 조건으로 탐색한다"는 기능에서는 동아리명, 분과, 활동 분야, 최근 활동 시점 같은 항목이 나왔습니다. 이렇게 기능 단위로 필요한 데이터를 나열하고 나니 중복되는 항목이 보이기 시작했고, 그게 자연스럽게 테이블의 후보가 됐습니다.

3. ERD 작성

리스트업한 데이터를 보며 어떤 데이터가 어디에 있어야 하는지를 정하는 단계입니다.

여기가 가장 어려웠습니다. 수업에서 배운 정규화를 적용하려니, 중복을 줄이려고 테이블을 쪼갤수록 조회할 때 조인이 늘어나는 상충 관계가 계속 나왔습니다. 어디까지 쪼갤지 판단하는 게 쉽지 않았습니다.

결국 기준을 이렇게 잡았습니다.

  • 성격이 다른 데이터는 나눈다 - 자주 바뀌지 않는 사실과 계속 쌓이는 기록은 다른 테이블로
  • 조회 성능은 나중에 본다 - 지금 규모에서 조인 한두 단계는 문제가 되지 않는다

ERD를 이 정도 규모로 그려본 게 처음이라 AI의 도움을 많이 받았습니다. 다만 그대로 받아쓰기보다는 "왜 이 테이블을 나눴는지"를 계속 물어보면서 이해하고 넘어가려고 했습니다.

4. RDS로 데이터베이스 만들기

스키마가 정해졌으니 실제 DB를 만들 차례입니다.

Supabase와 AWS RDS를 두고 고민했습니다. Supabase가 훨씬 간단했지만, AWS를 공부하고 싶은 마음과 추후 EC2·S3까지 함께 쓸 것을 생각해 RDS로 결정했습니다.

AWS는 예전에 실습용으로 조금 만져본 게 전부였고, RDS는 이번이 처음이었습니다.

4-1. 엔진 선택 : PostgreSQL

MySQL이 아니라 PostgreSQL을 골랐습니다. 이유는 세 가지입니다.

반정형 데이터를 다룹니다. 문서에서 추출한 결과나 AI 응답 원본을 그대로 보관해야 해서 JSONB가 필요했습니다.

제약으로 무결성을 지키고 싶었습니다. 특히 "동아리당 현재 버전은 1건"이라는 규칙을 이렇게 걸 수 있습니다.

CREATE UNIQUE INDEX one_current_profile_per_club
    ON club_profile (club_id) WHERE is_current;

이런 부분 유니크 인덱스는 MySQL에 없습니다. 애플리케이션 코드로 막아야 하는데, 그러면 언젠가 빠뜨리게 됩니다.

나중에 벡터 검색을 붙일 가능성이 있습니다. PostgreSQL이면 pgvector 확장을 켜는 것으로 끝납니다.

4-2. 식별자와 자격 증명

DB 인스턴스 식별자, 마스터 사용자명, 비밀번호를 설정합니다. 이 셋이 나중에 접속할 때 쓰이므로 따로 적어둬야 합니다.

4-3. 인스턴스와 스토리지

템플릿에서 프리 티어를 고르면 인스턴스 크기와 스토리지가 자동으로 최소 사양이 됩니다. 따로 건드리지 않았습니다.

한 가지, 스토리지 자동 확장은 꺼두었습니다. 켜두면 모르는 사이에 용량이 늘어나 요금이 붙을 수 있다길래...

4-4. VPC

이 DB를 어느 네트워크 안에 둘지 정하는 단계입니다. 기본 VPC를 그대로 골랐습니다.

주의할 점은 나중에 만들 EC2도 같은 VPC에 있어야 한다는 것입니다. VPC가 다르면 서로 통신이 되지 않습니다.

4-5. 보안 그룹

퍼블릭 액세스를 "예"로 설정했습니다. 로컬 PC에서 접속해 개발하기 위해서입니다.

보안 그룹은 기본(default) 대신 새로 생성했습니다. 기본 그룹은 다른 리소스도 함께 쓰기 때문에, DB 전용 그룹을 따로 두는 편이 관리하기 깔끔합니다.

여기서 헷갈렸던 부분이 있습니다. 보안 그룹은 RDS에 붙어 있지만, 규칙을 수정하려면 EC2 콘솔의 '보안 그룹' 메뉴로 가야 합니다. RDS 화면 안에서 찾다가 한참 헤맸습니다.

RDS가 보안 그룹을 만들면서 인바운드 규칙에 PostgreSQL / 5432 / 현재 내 공인 IP를 자동으로 넣어줍니다. 편하긴 한데, 여기서 문제가 하나 있습니다.

집 와이파이의 공인 IP는 고정이 아닙니다. 공유기를 재부팅하거나 학교·카페로 자리를 옮기면 IP가 바뀌고, 그러면 접속이 막힙니다. 그때마다 인바운드 규칙에서 소스를 "내 IP"로 다시 선택해줘야 합니다.

자주 쓰는 장소의 IP를 규칙으로 미리 등록해두면 갱신 빈도를 줄일 수 있습니다. 근본적인 해결은 EC2를 만든 뒤 소스를 IP 대신 EC2의 보안 그룹 ID로 바꾸는 것인데, 아직 배포 전이라 일단 이대로 두고 넘어갔습니다.

4-6. 모니터링

기본값 그대로 두었습니다.

5. 테이블 만들기

터미널에서 psql로 접속해 스키마를 올렸습니다.

psql 설치

# macOS
brew install libpq && brew link --force libpq

Windows는 PostgreSQL 설치 프로그램을 실행한 뒤 Command Line Tools만 선택하면 됩니다. 서버까지 설치할 필요는 없습니다.

접속 확인

psql "postgresql://마스터사용자명:비밀번호@엔드포인트:5432/postgres"

여기서 엔드포인트가 어디 있는지 몰라 한참 찾았습니다.

RDS 콘솔 → 데이터베이스 → 식별자 클릭 → 연결 & 보안 탭 → 엔드포인트 및 포트 항목에 있습니다. 목록 화면에는 나오지 않고, 상세로 들어가야 보입니다.

스크린샷 2026-09-16 오후 8.05.06.png

.rds.amazonaws.com으로 끝나는 주소 전체가 엔드포인트입니다. 저는 처음에 가운데 일부만 복사해서 "호스트 이름을 IP 주소로 바꿀 수 없음" 에러를 봤습니다. 손으로 옮겨 적지 말고 복사 아이콘을 쓰는 게 좋습니다.

데이터베이스 생성

접속한 뒤 서비스용 데이터베이스를 만듭니다.

CREATE DATABASE ssumate;
\q

여기서 만드는 건 테이블이 아니라 데이터베이스입니다. 계층이 이렇게 됩니다.

RDS 인스턴스          ← 서버 한 대. 요금이 붙는 단위
 └─ 데이터베이스 (ssumate)
     └─ 테이블 18개

인스턴스 하나 안에 데이터베이스를 여러 개 만들 수 있고, 요금은 인스턴스 단위라 데이터베이스를 몇 개 만들든 같습니다. 개발용과 테스트용을 나누고 싶다면 인스턴스가 아니라 데이터베이스를 두 개 만들면 됩니다.

스키마 적용

ERD를 기반으로 작성한 DDL 파일을 새로 만든 데이터베이스에 적용합니다.

psql "postgresql://마스터사용자명:비밀번호@엔드포인트:5432/ssumate" -f ssu_mate_ddl.sql

마지막 /ssumate가 데이터베이스 이름입니다. postgres가 아니라 방금 만든 것을 가리켜야 합니다.

확인

psql "postgresql://마스터사용자명:비밀번호@엔드포인트:5432/ssumate" -c "\dt"

300

테이블 18개가 모두 들어간 것을 확인했습니다. 스키마가 public으로 표시되는데, 이건 "공개"라는 뜻이 아니라 PostgreSQL의 기본 네임스페이스 이름입니다. 접근 제어는 보안 그룹과 DB 비밀번호가 담당하므로 이 이름과는 무관합니다.


마치며

모바일 개발로 서비스를 만들어본 적은 있지만, 기능명세서와 ERD를 이만큼 상세히 써본 건 처음이었습니다.

그래서 이것저것 많이 찾아보며 어떻게 해야 서비스를 효율적으로 굴릴 수 있을지 고민했습니다. 생각보다 서비스 볼륨이 큰 편이라 지금의 ERD가 맞는지는 아직 확신이 서지 않습니다. 다만 만들어가면서 계속 확인하고 고쳐보려 합니다.

다음은 실제 데이터를 넣고 백엔드를 붙이는 차례입니다. 그 과정도 정리해서 올려보겠습니다.