
개발하게 된 계기
그렇다고 뱅크샐러드나 가계부앱을 쓰자니... 기능도 너어무 많고 복잡해서...
진짜 내가 딱 필요한 기능이나 조회 기능만 넣은 웹을 개발하고 싶다 생각했다.
돈이 너무 쌓이다보니, 정리도 안 되고,,, 가계부란,, 어짜피 일일이 입력해야하는 번거로움은 존재할 수 밖에 없기 때문에 .. 아무튼 그냥 내가 필요한 가계부 하나 개발하자 생각했다.
개발 준비!
우선, 프론트엔드는 리액트 / 백엔드는 Node.js / DB는 sql lite 쓰려고 한다.
그냥 나만 쓸거라서 ㅋㅋ 배포 도메인은 github로 하려고 한다.
오랜만의 개발 프로젝트라서 조금 부족할 수 있겠지만... 열심히 개발해봐야겠다.
IDE는 당연히 VSC 사용.
NODE 버전은 22.어쩌구 쓴다
먼저 요구사항정의 부터 해야겠다.
Rough 하게 내가 원하는 화면별 기능이 무엇일까 생각해보았고, 대충 아래와 같이 짜보았다.
1. 메인 페이지에는 자산현황표가 나옴.
- 크게 카테고리별로 나누어져 있음
- 월급 / 국내주식 / 해외주식 / 코인 / ETF / 채권 / 예금 / 적금 / 파킹통장
- 첫페이지에 뜨는 데이터는 초기에는 모두 0원으로.
2. 데이터를 입력할 '초기 자산현황 입력' 페이지는 상단 메뉴바에서 초기입력 페이지를 들어갈 수 있게.
- 이 페이지는 처음에만 입력하면 되는 초기데이터임. 매월 입력하는 값들을 초기 데이터에 더해서 메인페이지에 보여줄 것임.
3. '초기 자산현황 입력' 페이지에서는 월급 / 국내주식 / 해외주식 / 코인 / ETF / 채권 / 예금 / 적금 / 파킹통장 이 카테고리별로 항목을 추가하고 각각 금액을 입력할 수 있음.
- 상태관리가 중요.
- 여기서 입력한 값들을 바탕으로 메인페이지에 카테고리별 항목 포함해서 값도 반영되어 띄워주기
4. 메인페이지에 스크롤 내려서 볼 수 있도록 하며, 스크롤을 내려보면 각 월마다 지출 금액들을 보여주기
5. '매월 현황 입력' 페이지도 상단 메뉴바에서 들어갈 수 있게.
6. 이 페이지에서는 1월부터 12월까지 입력할 수 있게 되어 있고.
7. 초기 데이터로 입력했던 항목 그대로 나열되어 있고, 월급 / 국내주식 / 해외주식 / 코인 / ETF / 채권 / 예금 / 적금 / 파킹통장 이 카테고리에서 추가할 수 있어야 함.
- 예를들어 해외주식 아래에 여러 종목이 이미 있었는데 매월 이게 달라질 수 있으니 기존 항목을 삭제 하거나 추가할 수 있게.
8. 입력 페이지들에 있는 예금 적금 카테고리는 개설일과 만기일을 선택할 수 있는 것도 필요할 거 같음.
9. 메인페이지 가장 큰 텍스트로 총 자산이 나옴.
- 자산현황표 돈을 다 더하고 지출 페이지에 입력된 값들을 빼면 총자산이 되도록.
10. ,,,,
.....
이런식으로 여러가지 항목을 엑셀에 작성하여 정리했다.
이렇게 해야 나중에 컴포넌트로 개발 할 때 헷갈리지 않음
괜히 요구사항 정의서 작성 단계가 있는 게 아님
화면정의서와 UI 레퍼런스
화면 정의서라기 보다는 UI레이아웃을 대충 짜주는 느낌이다. FIGMA로 작업을 했다.
이 단계를 부르는 용어가 있었는데 까먹음
어쨌든 화면은 저렇게 구성을 하고, 아래와 같이 화면 FLOW도 표현해줬다.
개발 플젝 안 한지 한참 된 직장인이지만 대학생 때 그렇게 빡쎄게 했던 개발 플젝 머슬 메모리가 남아있는게 웃기다 ㅋㅋ
레퍼런스는?
웃기지만 DEEPSEEK 나 챗지피티같은 깔끔한 스타일로 가고 싶어서... 딥시크랑,, 토스를 UIUX 레퍼런스로 잡았다.
미니멀리즘 체고 체고
REACT 앱 초기 셋업 / 백엔드 (Node + Express +SQLite) 기본 구조 셋팅
- 프로젝트 폴더 생성 (asset-management)
- 고 폴더에 들어가서
npx create-react-app frontend - 다 했으면 frontend 폴더 있는 경로에서 backend 폴더 만들어주기
- backend 폴더에서
npm init -y //package.json 생성
npm install express cors sqlite3
=> express 는 Node.js에서 서버를 구성하는 거고
=> cors 는 다른포트에서 접근 시 CORS(Cross-Origin Resource Sharing ) 에러 방지 해주려고 하는거임 - 서버 구동용으로 이제 스크립트를 추가해줘야겠지
>> package.json 안에 "scripts" 항목에 아래와 같이 추가해주면 됨.
{
"scripts": {
"start": "node index.js"
}
}
6. 자 다 했으면 backend 폴더 안에 index.js 라는 이름으로 파일을 생성해주자.
const express = require('express');
const cors = require('cors');
const app = express();
const port = 4000;
// 미들웨어 설정
app.use(cors());
app.use(express.json());
// 간단한 라우터 예시
app.get('/', (req, res) => {
res.send('Hello from Express with SQLite');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
요 상태에서 npm start 를 하면 서버가 구동됨.
SQLite DB 파일 연결하기
database.db랑 db.js 도 파일 추가해주자.
여기서 디렉토리 구조 한번 짚고 가자.
my-asset-management
├─ frontend // React 프로젝트
├─ backend
│ ├─ database.db // SQLite DB 파일 (빈 파일 생성해두거나 자동 생성 예정)
│ ├─ db.js // DB 연결 로직
│ ├─ index.js // Express 서버 진입점
│ └─ package.json
└─ ...
- database.db는 빈 파일이면 됨 나중에 index.js에서 db.run 함수 써서 스키마를 자동 생성해줄 것임.
- db.js (DB연결 설정을 들어가보자)
// backend/db.js
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
// database.db 파일의 절대 경로 가져오기
const dbPath = path.resolve(__dirname, 'database.db');
// SQLite DB 연결
const db = new sqlite3.Database(dbPath, (err) => {
if (err) {
console.error('데이터베이스 연결 실패:', err);
} else {
console.log('SQLite 데이터베이스에 연결되었습니다.');
}
});
module.exports = db;
- sqlite 패키지를 require 한 뒤 verbose()모드로 사용
- path.resolve(__dirname, 'database.db') 로 현재 디렉토리 기준의 절대 경로를 만들어서 DB에 연결하는거
- DB 인스턴스를 module.exports로 내보내서 다른 파일에서 가져다 쓰면 됨.
간단하게 API 구축 해보자
백엔드의 꽃! (??) API 구축
일단 아까 해주겠다던 DB 스키마를 만들어놓자..
db.serialize(() => {
// 예: 자산(Assets) 테이블 (월급/주식/코인/채권 등등 구분 용도)
db.run(`
CREATE TABLE IF NOT EXISTS assets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT, -- 자산 카테고리 (예: '월급', '국내주식', '코인' 등)
name TEXT, -- 종목명 or 상세명 (예: '삼성전자', '비트코인', ...)
amount INTEGER, -- 자산 금액
month INTEGER, -- 몇 월 자산인지 (1~12)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// 예: 지출(Expenses) 테이블
db.run(`
CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT, -- 지출 카테고리 (예: '송금', '식비', '화장품쇼핑' 등)
description TEXT, -- 지출 상세 설명
amount INTEGER, -- 지출 금액
month INTEGER, -- 몇 월 지출인지 (1~12)
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
});
일단 동작이 되는지 화면으로 확인하기 위해 문구도 하나 띄워주는 테스트 라우터도 하나 두고..
app.get('/', (req, res) => {
res.send('Hello from Express + SQLite');
});
# CRUD API 를 만들건데 음... 자세한 코드는 깃헙에서 보시기를
// 1) 모든 자산 조회
app.get('/api/assets', (req, res) => {
const sql = 'SELECT * FROM assets';
db.all(sql, (err, rows) => {
if (err) {
return res.status(500).json({ error: err.message });
}
res.json(rows); // 자산 목록 응답
});
});
// 2) 자산 하나 추가
app.post('/api/assets', (req, res) => {
// body에서 데이터 꺼내기
const { category, name, amount, month } = req.body;
const sql = `
INSERT INTO assets (category, name, amount, month)
VALUES (?, ?, ?, ?)
`;
db.run(sql, [category, name, amount, month], function (err) {
if (err) {
return res.status(500).json({ error: err.message });
}
// 새로 생성된 레코드의 ID는 this.lastID 로 얻을 수 있음
res.json({
id: this.lastID,
category,
name,
amount,
month
});
});
});
// 3) 특정 자산 수정
app.put('/api/assets/:id', (req, res) => {
const { id } = req.params;
const { category, name, amount, month } = req.body;
const sql = `
UPDATE assets
SET category = ?, name = ?, amount = ?, month = ?
WHERE id = ?
`;
db.run(sql, [category, name, amount, month, id], function (err) {
if (err) {
return res.status(500).json({ error: err.message });
}
if (this.changes === 0) {
return res.status(404).json({ error: '자산이 존재하지 않습니다.' });
}
res.json({ success: true });
});
});
// 4) 특정 자산 삭제
app.delete('/api/assets/:id', (req, res) => {
const { id } = req.params;
const sql = 'DELETE FROM assets WHERE id = ?';
db.run(sql, id, function (err) {
if (err) {
return res.status(500).json({ error: err.message });
}
if (this.changes === 0) {
return res.status(404).json({ error: '자산이 존재하지 않습니다.' });
}
res.json({ success: true });
});
});
중간 중간 들어간 ???? 물음표 투성이는 파라미터를 바인딩하기 위한 플레이스 홀더 임. 프론트엔드 하는 사람이면 플ㄹ이스홀더면 바로 알아들어야 할 것. 그냥 입력?접수 받을 예정이라는 것. 나중에 전달 받으면 넣는다~ 이런 느낌.
왜 이렇게 쓰냐 하면....
SQL 인젝션을 방지 하기 위해서임.
문자열을 이어붙여서 쿼리를 그대로 작성해버리면 해커가 특수문자나 SQL 문법을 넣어 공격이 가능하기 때문...
사실 거대한 웹서비스를 개발하는게 아니라서 대충하면 되긴 하는데, 예전에 작업했던 메모리가 있어서...
뭐 준비된 쿼리에 파라미터만 교체해가며 재사용 할 수 있으니 그것도 좋긴 함.
다음 편에 이어서...
'🛠개발 일지' 카테고리의 다른 글
나만의 AI 가계부 웹개발하기 (REACT, Node.js) 5탄 - 메인 페이지 연동 확인 및 매월 현황 입력 페이지 구현 (0) | 2025.03.03 |
---|---|
나만의 AI 가계부 웹개발하기 (REACT, Node.js) 4탄 - 초기자산입력 페이지 구현, 주식 카테고리는 주가 반영 (0) | 2025.02.16 |
나만의 AI 가계부 웹개발하기 (REACT, Node.js) 3탄 - 메인페이지 리액트 컴포넌트 개발 (0) | 2025.02.09 |
PdfMathTranslate 번역 오픈소스 사용해보기 - 설치, 사용방법 및 결과 분석 (매우 EASY) (1) | 2025.02.02 |
나만의 AI 가계부 웹개발하기 (REACT, Node.js) 2탄 - SQLite DB 연동 / 관리 (2) | 2025.01.30 |