아침소리 (211.♡.103.115)
2026년 4월 8일 PM 04:54
하... 이거 마르지 않는 샘물이에요.
디버깅 이슈가 거의 없이 기능 추가, 수정이 되니 30년간 직딩하면서 고민하던거 다 때려박는 중입니다.
장비 관리, AS 기능 추가, 결제 포함 근태 관리 및 공유 등등..
일주일만 여기 전념하면 진짜 모든 직딩이 원하는 erp 나올거 같아요.
감히 이렇게 얘기할 수 있는건 대기업 신입 시절부터 개발부서 이면서도 이런 tf에 계속 불려다니면서 맨날 망하는거만 봐서..
대기업 -> 벤쳐 -> 중소수입사 -> 중기업 다니면서 어디서도 해결 못하던건데..
진짜 직원들의 일을 도와주면서 공유하는 시스템을 만들어내고 있습니다.
노션, 팀즈, 다 필요없을 정도로 갈수 있을거 같아요.

방금 작업 하고 있는 클로드코드 화면입니다.
● 완료. 관리 메뉴에 시스템 이력 페이지를 추가했습니다.
구성:
- 백엔드: GET /api/v1/activities — ADMIN 전용, 페이지네이션(30건씩), 유형/검색 필터
- 프론트엔드: /admin/activity-logs — 테이블 형태로 시간, 유형(배지), 프로젝트, 내용, 사용자 표시
- 필터: 유형 드롭다운 (태스크 생성/삭제, 상태변경, 진행률, 일정, 배정, 댓글 등 11종) + 설명 텍스트 검색
- 관리 메뉴: 결재라인 아래 "시스템 이력" 항목 추가
현재 450건의 기존 이력이 바로 조회됩니다. createdAt DESC 인덱스 + 페이지네이션으로 부하는 무시할
수준입니다.
✻ Cooked for 4m 26s
> 시스템 이력에서 사용자를 ID로 표시할 필요있나?
● 맞습니다. 이메일 앞부분 대신 사용자 이름으로 표시하겠습니다. activity_logs의 userId에 실제로 어떤 값이
저장되는지 확인합니다.
● Bash(docker exec erp-ot-postgres psql -U erp_user -d erp_ot -t -A -c "
SELECT DISTINCT \"userId\" FROM project.activity_logs LIMIT 10;")
⎿ cmmx5lkhq000ichn44ceb8s7v
cmmyi7dcl00026314d6x7evzn
user-admin1
… +7 lines (ctrl+o to expand)
● userId는 UUID와 문자열이 혼재합니다. 백엔드에서 사용자 이름을 join해서 내려주겠습니다.
Read 1 file (ctrl+o to expand)
● auth-service에 내부 API로 사용자 이름을 일괄 조회하는 것보다, 같은 쿼리에서 처리하는 게 낫습니다.
activity_logs의 userId로 auth_users를 직접 조회할 수 없으므로(다른 스키마), raw query 또는 프론트에서
사용자 목록을 매핑합니다. 프론트에서 이미 userManagementApi.list()로 사용자 목록을 가져올 수 있으니 그
방식이 가장 간단합니다.
● Update(apps\web\src\app\admin\activity-logs\page.tsx)
⎿ Added 1 line, removed 1 line
2
3 import { useEffect, useState } from "react";
4 import { useRouter } from "next/navigation";
5 -import { activityLogApi, getUser } from "@/lib/api";
5 +import { activityLogApi, userManagementApi, getUser } from "@/lib/api";
6
7 const ACTION_LABELS: Record<string, { label: string; color: string }> = {
8 TASK_CREATED: { label: "태스크 생성", color: "bg-blue-100 text-blue-700" },
● Update(apps\web\src\app\admin\activity-logs\page.tsx)
⎿ Added 11 lines
43 const [actionFilter, setActionFilter] = useState("");
44 const [search, setSearch] = useState("");
45 const [searchInput, setSearchInput] = useState("");
46 + const [userMap, setUserMap] = useState<Map<string, string>>(new Map());
47
48 useEffect(() => {
49 const user = getUser();
50 if (!user || user.role !== "ADMIN") { router.replace("/projects"); return; }
51 + // 사용자 ID→이름 매핑 로드
52 + userManagementApi.list().then((data) => {
53 + const users: any[] = Array.isArray(data) ? data : ((data as any).items ?? []);
54 + const map = new Map<string, string>();
55 + for (const u of users) {
57 + if (u.email) map.set(u.email, u.name);
58 + }
59 + setUserMap(map);
60 + }).catch(() => {});
61 }, []);
62
63 useEffect(() => {
● Update(apps\web\src\app\admin\activity-logs\page.tsx)
⎿ Added 1 line, removed 1 line
172 <div className="text-[11px] text-gray-400 mt-0.5">태스크: {meta
.taskName}</div>
173 )}
174 </td>
175 - <td className="px-3 py-2 text-xs text-gray-500 whitespace-nowrap">{
-item.userId?.split("@")[0] ?? "-"}</td>
175 + <td className="px-3 py-2 text-xs text-gray-500 whitespace-nowrap">{
+userMap.get(item.userId) ?? "-"}</td>
176 </tr>
177 );
178 })}
● 완료. 사용자 ID/이메일 대신 이름으로 표시하도록 변경했습니다. 페이지 로딩 시 사용자 목록을 한 번
가져와서 ID→이름 매핑을 만들고, 각 이력 행에서 이름으로 보여줍니다.
✻ Worked for 1m 5s
> 시간은 연월일시분초 로..
● Update(apps\web\src\app\admin\activity-logs\page.tsx)
⎿ Added 7 lines, removed 11 lines
20
21 function formatDate(iso: string) {
22 const d = new Date(iso);
23 - const now = new Date();
24 - const diffMs = now.getTime() - d.getTime();
25 - const diffMin = Math.floor(diffMs / 60000);
26 - if (diffMin < 1) return "방금 전";
27 - if (diffMin < 60) return ${diffMin}분 전;
28 - const diffHour = Math.floor(diffMin / 60);
29 - if (diffHour < 24) return ${diffHour}시간 전;
30 - const diffDay = Math.floor(diffHour / 24);
31 - if (diffDay < 7) return ${diffDay}일 전;
32 - return d.toLocaleDateString("ko-KR", { year: "numeric", month: "2-digit", day: "2-digi
-t" })
33 - + " " + d.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit" });
23 + const y = d.getFullYear();
24 + const m = String(d.getMonth() + 1).padStart(2, "0");
25 + const dd = String(d.getDate()).padStart(2, "0");
26 + const hh = String(d.getHours()).padStart(2, "0");
27 + const mm = String(d.getMinutes()).padStart(2, "0");
28 + const ss = String(d.getSeconds()).padStart(2, "0");
29 + return ${y}-${m}-${dd} ${hh}:${mm}:${ss};
30 }
31
32 export default function ActivityLogsPage() {
● 완료. 2026-04-08 16:50:06 형식으로 표시됩니다.
> 현재 도커 이미지 크기가 얼마지?
● Bash(docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep -i erp)
⎿ erp_ot-project-service latest 504MB
erp_ot-equipment-service latest 500MB
erp_ot-attendance-service latest 498MB
… +4 lines (ctrl+o to expand)
● ┌────────────────────┬──────────┐
│ 이미지 │ 크기 │
├────────────────────┼──────────┤
│ auth-service │ 535MB │
├────────────────────┼──────────┤
│ project-service │ 504MB │
├────────────────────┼──────────┤
│ equipment-service │ 500MB │
├────────────────────┼──────────┤
│ attendance-service │ 498MB │
├────────────────────┼──────────┤
│ user-service │ 487MB │
├────────────────────┼──────────┤
│ web (Next.js) │ 224MB │
├────────────────────┼──────────┤
│ 합계 │ 약 2.7GB │
└────────────────────┴──────────┘
백엔드 서비스들이 각 500MB 내외로 큰 편입니다. Node.js 베이스 이미지 + Prisma 엔진 + node_modules가
원인입니다. 필요하면 multi-stage 빌드 최적화로 절반 이하로 줄일 수 있습니다.
> 현재 이력은 프로젝트 관련만 있는데 다른 부분도 넣을수 있나? 넣을 수 있다면 어떤 내용이 필요하지?
● 현재 각 서비스에서 이력으로 남길 수 있는 항목을 정리하겠습니다.
현재 상태
- project-service: 이미 activity_logs 테이블에 기록 중 (450건)
추가 가능한 이력
┌────────────────────┬───────────────────────────┬─────────────────────────┬─────────────┐
│ 서비스 │ 이벤트 │ 중요도 │ 구현 난이도 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ auth-service │ 로그인 성공/실패 │ 높음 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 사용자 생성/수정/비활성화 │ 높음 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 비밀번호 초기화 │ 높음 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 부서 생성/수정/삭제 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 결재라인 변경 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 권한(role) 변경 │ 높음 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ attendance-service │ 출퇴근 기록 │ 낮음 (이미 별도 테이블) │ - │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 휴가 신청/승인/반려 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 초과근무 신청/승인 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ equipment-service │ 장비 등록/수정/폐기 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 수리 접수/상태변경 │ 중간 │ 낮음 │
├────────────────────┼───────────────────────────┼─────────────────────────┼─────────────┤
│ │ 센서 배치/회수 │ 낮음 │ 낮음 │
└────────────────────┴───────────────────────────┴─────────────────────────┴─────────────┘
추천: 우선 넣을 것
auth-service 이력이 가장 가치 있습니다:
1. 로그인 이력 — 보안 감사, 마지막 접속 확인
2. 권한/역할 변경 — 누가 언제 누구 권한을 바꿨는지
3. 사용자 생성/비활성화 — 인사 변동 추적
4. 부서/결재라인 변경 — 조직 변경 이력
구현 방식
두 가지 옵션이 있습니다:
┌────────────────────────────────────────┬─────────────────┬───────────────────────────────────────┐
│ 방식 │ 장점 │ 단점 │
├────────────────────────────────────────┼─────────────────┼───────────────────────────────────────┤
│ A. 각 서비스 자체 DB에 기록 │ 간단, 서비스 │ 통합 조회 시 여러 DB 호출 │
│ │ 독립 │ │
├────────────────────────────────────────┼─────────────────┼───────────────────────────────────────┤
│ B. project-service의 activity_logs에 │ 한 곳에서 조회 │ 서비스 간 의존성 (내부 API 또는 │
│ 통합 │ │ RabbitMQ) │
└────────────────────────────────────────┴─────────────────┴───────────────────────────────────────┘
B 방식 + RabbitMQ가 좋습니다. 이미 RabbitMQ가 docker-compose에 있고, 각 서비스가 이벤트를 publish하면
project-service가 consume해서 activity_logs에 저장하면 됩니다. 비동기라 부하 영향도 없습니다.
auth-service 이력부터 넣을까요?
> B방식으로 하자.
● 먼저 현재 RabbitMQ 설정을 확인합니다.
Searched for 1 pattern, read 1 file (ctrl+o to expand)
● project-service만 RABBITMQ_URL이 있네요. auth-service에도 추가해야 합니다.
Reading 1 file… (ctrl+o to expand)
⎿ docker-compose.yml
✻ Compacting conversation… (2m 35s · ↑ 64 tokens · thought for 2s)
⎿ Tip: Use /btw to ask a quick side question without interrupting Claude's current work
댓글 (7)
-
DDEFT
04.08 · 121.♡.110.211
- 아
아침소리
→ DEFT 작성자
04.08 · 211.♡.103.115
바이브코딩 만으로 개발중입니다. 클로드코드 + bkit 이라는 플러그인 입니다.
엔터프라이즈 급으로 최초 사양을 만들라고 했더니 도커에 백앤드와 DB를 넣고 프론트앤드로 구성이 된다네요.
-> 저는 암것도 모릅니다.ㅎㅎㅎ
디지인이고 뭐고 암것도 모르고 기능만 상세히 정의해서 진행했는데 여기까지 왔네요.
대략 3주정도 걸렸습니다. 업무 짬짬히 혼자 하면서요..
-
육육류
04.08 · 211.♡.79.130
필요는 한데 만들 시간이 없어서 도전은 못하고 있습니다...
-
마마이너스아이
04.08 · 61.♡.139.51
저랑 같은 테크 트리를 타고 계시는 군요.
30년 넘게 하던 일이 있는데 다 때려 넣고 있습니다.
이제는 심지어 각종 메뉴얼, 제품소개서, 세일즈킷, crm 까지 혼자서 다하고 있습니다. 무섭습니다.
-
비비눗방울
04.08 · 223.♡.43.96
멋지십니다. 기능은 개발하시는 것보다 덜하지만 저도 사내시스템 오픈했습니다. 아주 만족중입니다.
이제 좀 더 붙여볼 내용을 고민중입니다.^^ -
남남극백곰
04.08 · 223.♡.47.77
이게 뭐죠??? 넘 어지러워요... ㅠㅠ
-
아아리니아빠
04.08 · 121.♡.83.61
저도 하고 있는데, 만드신 것 보니 그룹웨어 + 워크플로우 관리 다 때려 박아야 하는 고민에 빠졌습니다.
괜히 봤네요 ㅠㅠ
댓글을 작성하려면 이 필요합니다.
웹어플리케이션으로 만드시는 건가요?. 플랫폼이 궁금합니다.
저는 microsoft ACCESS와 mysql 갖고 만들고 있거든요.