■ 소개
목표: 419 (4업으로 1달러 벌기 시작해서 9달러까지 벌어보기)
https://crisp-curry-60549088.figma.site/
.
어제에 이어서 마저 관계형 데이터 베이스 구축 유튜뷰를 보고
제 나름대로 만든 다음 커서를 통해 보완받아봤습니다.
.
그리고 앱인토스에 어거지로 일단 등록 신청해보고 반려사유 받아보기를 했습니다!
.
■ 진행 및 결과
1) DB구조 다시 짜기
유튜브 보고 5개 ENTITY로 구성했는데,
커서한테 조언을 구하니 2개로 줄여줬습니다.
받은 조언은 아래와 같습니다.
ERD 및 프롬프트 개선 제안
1. MYPAGE 엔티티의 정체성 (테이블 분리 vs 통합)
현재: USER와 별도로 MYPAGE가 있고 여기에 TOTALVOTES, SUCCESSRATE 등이 있습니다.
문제: 관계형 DB에서 MYPAGE라는 테이블 명은 어색합니다. 이는 보통 USER_STATS (유저 통계) 라는 이름으로 USER와 1:1 관계를 맺거나, 트래픽이 적다면 USER 테이블에 컬럼으로 포함시킵니다.
제안: 프롬프트에 "MYPAGE는 USER_STATS 테이블로 명명하고, User 테이블과 1:1 관계를 맺는다"고 명시하는 것이 좋습니다.
2. REMAINTIME (남은 시간) 속성
현재: REMAINTIME을 INT로 저장하려고 합니다.
문제: DB에 '남은 시간(예: 60분)'을 저장하면 1분마다 모든 데이터를 업데이트해야 합니다.
제안: DEADLINE (마감 시간, DateTime)을 저장하고, 남은 시간은 쿼리 시점(NOW())에 계산하도록 변경해야 합니다.
3. RESULT와 ESG ISSUE 관계
현재: ESG ISSUE와 RESULT가 분리되어 있습니다.
분석: 하나의 이슈에 결과는 하나뿐이라면 굳이 테이블을 나눌 필요가 없습니다. (조인 비용 발생)
제안: ESG_ISSUE 테이블에 result_status, actual_change_rate 등의 컬럼을 추가하여 합치는 것이 효율적일 수 있습니다. 다만, ERD를 그대로 따르길 원하신다면 1:1 관계로 명시해야 합니다.
4. UID, IID 등 모호한 약어
제안: SQL 표준 관례에 따라 user_id, issue_id, comment_id 등으로 명확하게 작성하도록 지시하는 것이 좋습니다. 특히 Toss userKey를 사용할 것이므로 user_id는 BIGINT가 되어야 할 수도 있습니다..
요걸 FIREBASE용으로 다음은 MD는 다음과 같습니다.
# UPDOWNESG Firestore DB Schema
이 문서는 UPDOWNESG 프로젝트의 Firebase Cloud Firestore 데이터베이스 구조를 정의합니다.
ERD의 6개 엔티티(User, MyPage, EsgIssue, Result, Prediction, Comment)를 NoSQL 특성에 맞춰 최적화했습니다.
## 1. Collections Overview
| Collection Path | Description | ERD Mapping |
|-----------------|-------------|-------------|
| `users` | 사용자 정보 및 통계 | **USER** + **MYPAGE** |
| `users/{uid}/predictions` | 사용자의 투표/예측 기록 | **PREDICTION** |
| `issues` | ESG 이슈 정보 및 결과 | **ESG ISSUE** + **RESULT** |
| `issues/{issueId}/comments` | 이슈에 달린 댓글 | **COMMENT** |
---
## 2. Detailed Schema
### 2.1. `users` Collection
사용자의 기본 정보와 통계(MyPage) 정보를 통합하여 관리합니다.
* **Document ID**: `uid` (Firebase Auth UID)
```json
{
// [Basic Info]
"uid": "string (PK)",
"email": "string",
"nickname": "string",
"avatarUrl": "string",
"createdAt": "timestamp",
"lastLoginAt": "timestamp",
// [Stats - 구 MYPAGE]
// 읽기 효율을 위해 별도 컬렉션이 아닌 필드로 포함
"stats": {
"totalVotes": "number (총 투표수)",
"successCount": "number (예측 성공 횟수)",
"successRate": "number (성공률 %)",
"currentStreak": "number (현재 연속 성공)",
"maxStreak": "number (최대 연속 성공)",
"points": "number (보유 포인트)"
}
}
```
### 2.2. `issues` Collection
투표 대상인 ESG 이슈와 그 결과 정보를 하나의 문서에서 생명주기(Status)로 관리합니다.
* **Document ID**: `issueId` (Auto-generated ID)
```json
{
// [Issue Info - 구 ESG ISSUE]
"title": "string (헤드라인)",
"detail": "string (상세 내용)",
"category": "string ('E' | 'S' | 'G')",
"companyName": "string",
"companyLogo": "string (URL)",
// [Schedule]
"publishedAt": "timestamp (게시일)",
"deadline": "timestamp (투표 마감일)",
// [Status & Votes]
"status": "string ('voting' | 'calculating' | 'closed')",
"voteCounts": {
"up": "number",
"down": "number",
"total": "number"
},
// [Result - 구 RESULT]
// status가 'closed'일 때 유효한 값
"result": {
"targetOutcome": "string ('UP' | 'DOWN')", // 정답
"actualChangeRate": "number", // 실제 변동률
"finalizedAt": "timestamp"
}
}
```
### 2.3. `predictions` Sub-collection
* **Parent**: `users/{uid}`
* **Document ID**: `issueId` (중복 투표 방지를 위해 이슈 ID를 Key로 사용)
```json
{
"issueId": "string (FK)",
"vote": "string ('UP' | 'DOWN')",
"createdAt": "timestamp",
// 결과 처리 후 업데이트되는 필드
"status": "string ('waiting' | 'success' | 'fail')",
"earnedPoints": "number",
// [Snapshot]
// 목록 조회 시 Join 없이 UI를 그리기 위한 중복 데이터
"snapshot": {
"issueTitle": "string",
"companyName": "string",
"category": "string",
"deadline": "timestamp"
}
}
```
### 2.4. `comments` Sub-collection
* **Parent**: `issues/{issueId}`
* **Document ID**: `commentId` (Auto-generated ID)
```json
{
"content": "string",
"createdAt": "timestamp",
"likeCount": "number",
// [Writer Snapshot]
// 작성자 정보를 매번 join하지 않기 위함
"writer": {
"uid": "string",
"nickname": "string",
"avatarUrl": "string",
"vote": "string ('UP' | 'DOWN' | null)" // 작성 당시 투표 포지션
}
}
```
## 3. Indexing Strategy (참고)
* **기본 인덱스**: Firestore가 단일 필드에 대해 자동 생성.
* **복합 인덱스(Composite Index)** 필요 예상:
* `issues`: `status` + `deadline` (마감 임박순 정렬)
* `predictions`: `uid` + `createdAt` (내 활동 타임라인)
* `comments`: `issueId` + `createdAt` (댓글 최신순 정렬)
.
실제로 FIREBASE DB를 업데이트 해달라고 했고,
그 결과는 다음과 같습니다.
데이터 초기화가 끝나면 Firebase 콘솔을 새로고침해서 다음을 확인해보세요:
daily_logs 컬렉션이 사라졌는지 (이제 사용하지 않음)
루트 레벨의 predictions 컬렉션이 사라졌는지 (이제 users 하위로 이동)
users와 issues 컬렉션만 깔끔하게 남아있는지.
정확히 뭔지는 모르지만,
네트워크 방에서 정혜지 님께 배운데로
ERD 를 yaml 형식으로 바꿔서 https://dbdiagram.io/d에서 돌려본 결과물은 아래와 같습니다.
.
2) 앱인토스 반려사유 받아보기
며칠 전에 무작정 일단 앱을 등록해봤습니다.
반려사유를 준다고 하는데, 어제 밤에는 업무시간이 끝나서 회신 받지 못했습니다.
그래서 오늘 업무시간에 다시 여쭤봤습니다.
.
뭔가 사유를 말씀주실 줄 알았는데,
의외로
"구체적인 서비스 내용 및 목적"이 뭐냐고 물어보셨습니다.
다시 내부 확인 후 안내주신다고 합니다.
.
친절한 Q&A 시스템이 갖춰져있는 것 같아 마음이 든든합니다.
뭔가 막혀도 물어볼 구석이 있다는게 좋습니다.
.
3) 여담
사업자등록했습니다 ㅎㅎ 그냥 하루만에 뚝닥했고, 토스 로그인 등을 달기 위해 대표관리자로 신청해두었습니다
■ 느낀점 및 향후 계획
이왕 사업자등록까지 한김에 조금 더 고삐를 죄어서
출시까지는 안되더라도 계속해서 앱인토스 검토요청을 넣어볼 생각입니다.
1달러 벌어보자 ㅎㅎ