사내·소규모 팀용 셀프호스팅 채팅 — Discord/Slack 의 미니 사촌. IP 기반 접근 제어, 그룹/채널 권한, 사용자 프로필, 리액션, 메시지 편집, 멀티 테마.
npm install # 최초 1회
npm start # → http://localhost:3000| 화면 | 경로 |
|---|---|
| 채팅 | http://localhost:3000 |
| 관리자 패널 | http://localhost:3000/admin |
| 말풍선 단위테스트 | http://localhost:3000/bubble-test.html |
- 실시간 메시지/파일 (WebSocket + SQLite)
- 마크다운 (
**굵게**,`코드`, 리스트, 인용, 링크 등) - 이미지 인라인 미리보기 + 라이트박스
- 파일 첨부 (드래그&드롭 / 첨부 버튼)
- 입력 중 / 업로드 중 인디케이터
- 메시지 편집 — hover 시 ✏ 버튼으로 인라인 편집, 편집된 메시지는 시간 옆에 (편집됨) 표시
- 본인 메시지는 본인이 편집
- 관리자는 모든 메시지 편집 가능
- Enter 저장 · Shift+Enter 줄바꿈 · Esc 취소
- allowedIPs — 접속 허용 IP (와일드카드 / 범위 / 콤마 목록 지원)
- groups — IP를 그룹으로 묶어 채널 권한 부여 (admin / winapp / guest 등)
- channels.access
guest— allowedIPs 내 모든 사용자private— 모두 접근 가능, 본인 메시지만 조회 (개인 메모용)admin— admin 그룹만 접근<그룹ID>또는["a","b"]— 해당 그룹(들)만 접근
- 관리자 설정 — 관리자 패널에서 IP 별 닉네임/아바타 부여
- 사용자 직접 설정 — 사이드바 푸터의 본인 아바타 클릭 → 닉네임 / 아바타 / 500자 자기소개 직접 편집 사용자 본인 설정이 관리자 기본값을 오버라이드합니다.
- 프로필 조회 — 채팅의 이름/아바타, 사용자 패널 항목 클릭 → 상세 프로필 모달 (온라인 상태, 마지막 접속, 방문수, 자기소개)
관리자 패널에서 허용 이모지 목록을 자유롭게 편집할 수 있습니다. 메시지 hover 시 우측 액션 툴바에서 클릭 → 토글.
사이드바 푸터의 ⚙ 기어 아이콘으로 전환하며 localStorage(concord-theme)에 저장됩니다.
| 테마 | 분위기 |
|---|---|
| Concord | 다크 / 퍼플 (기본·브랜드) |
| Claude | 다크 / 골드 |
| Office | 라이트 / 블루 |
Concord 테마는 영역 단계가 명확하도록 검은 톤들이 단계화되어 있습니다.
좌측 세로 사이드바 형태로 다음 탭을 제공합니다.
- 🔐 IP 관리 — 허용 IP / 그룹 편집
- 📡 채널 — 채널 추가/편집, 접근 권한 설정
- ⭕ 리액션 — 허용 이모지 편집
- 👥 사용자 — IP별 닉네임/아바타 (관리자 지정값)
- 📁 파일 — 업로드된 첨부 파일 일괄 관리/삭제
말풍선 렌더링 로직이 별도 모듈로 분리되어 있어 단독 수정·테스트가 가능합니다.
public/bubble.css— 말풍선 CSS 규칙public/bubble.js—BubbleModule.createBubble()순수 함수public/bubble-test.html— 10가지 케이스 (짧은/긴 텍스트, 멀티라인, 마크다운, 코드블록, 매우 긴 단어, 첨부, 연속 대화 등)를 3개 테마로 즉시 비교 가능
말풍선 관련 이슈가 보일 경우 위 두 파일을 수정 후 단위테스트 페이지에서 검증할 수 있습니다.
서버와 같은 폴더의 config.json이 모든 권한·표시 설정의 단일 소스입니다.
서버 재시작 없이 파일 저장만 해도 즉시 반영됩니다.
{
"allowedIPs": [
"127.0.0.1",
"::1",
"192.168.8.*",
"192.168.8.141-186",
"192.168.5.51"
],
"groups": {
"admin": { "label": "최고관리자", "ips": ["127.0.0.1", "192.168.8.141"] },
"winapp": { "label": "윈앱팀", "ips": ["192.168.8.141-186"] },
"guest": { "label": "게스트", "ips": ["192.168.8.*"] }
},
"channels": [
{ "id": "general", "type": "channel", "label": "일반", "desc": "자유롭게", "access": "guest" },
{ "id": "files", "type": "channel", "label": "파일", "desc": "파일 채널", "access": ["winapp"] },
{ "id": "admin-only", "type": "channel", "label": "관리자 전용", "desc": "...", "access": "admin" },
{ "id": "me", "type": "dm", "label": "나에게 메모", "desc": "개인 메모", "access": "private" }
],
"profiles": {
"192.168.8.141": { "name": "Admin", "avatar": "/uploads/141.png" },
"192.168.8.186": { "name": "Galaxy", "avatar": "/uploads/186.png" }
},
"allowedReactions": ["⭕", "❌", "✅", "👍", "👎", "❤️", "😂", "🔥"],
"port": 3000,
"fileSizeLimitMB": 100
}-
allowedIPs — 허용 IP. 목록에 없는 IP는 403으로 차단.
- 단일
192.168.1.10, 와일드카드192.168.1.*, 범위192.168.1.10-30, 콤마10.0.0.1,10.0.0.2
- 단일
-
groups — IP를 그룹으로 묶음.
admin그룹은 모든 채널에 자동 접근 + 다른 사용자 private 채널을 읽기 전용으로 열람 가능 + 모든 메시지 편집 가능
-
channels — 사이드바 채널 목록 (순서대로 표시).
id는 DB 저장 키 (한 번 정하면 변경 비권장)type:"channel"(#) /"dm"(@)access: 위 권한 모델 참고
-
profiles — IP 별 표시 닉네임/아바타 (관리자가 지정). 사용자가 본인 프로필을 직접 편집한 경우 그 값이 우선합니다.
-
allowedReactions — 메시지 리액션으로 사용할 이모지 목록.
| 위치 | 내용 |
|---|---|
void.db |
메시지 (편집 이력 포함)·첨부·리액션·접속 이력·user_profiles |
uploads/ |
첨부 파일 / 아바타 이미지 (자동 생성) |
config.json |
권한·관리자 프로필·채널 설정 |
DB 파일명은 레거시 호환을 위해
void.db를 유지합니다. 메시지 편집을 위해messages.edited_at컬럼이 자동 추가됩니다 (서버 시작 시 마이그레이션).
GET /api/whoami— 내 IP, 프로필, 채널, 그룹GET /api/messages/:channel— 채널 메시지POST /api/messages— 메시지/첨부 전송PATCH /api/messages/:id— 메시지 텍스트 수정 (본인 또는 admin)DELETE /api/messages/:id,DELETE /api/attachments/:idPOST /api/reactionsGET /api/users— 접속 이력 + 현재 접속자 (오른쪽 패널용)
GET /api/profile— 내 프로필 조회PATCH /api/profile— 닉네임 / 자기소개 (≤500자) 수정POST /api/profile/avatar— 본인 아바타 업로드 (≤5MB)DELETE /api/profile/avatar— 본인 아바타 제거 (관리자 기본값으로 복귀)
GET /api/users/:ip/profile— 다른 사용자 프로필 (읽기 전용)
GET/PATCH /api/admin/configGET /api/admin/users,PATCH /api/admin/users/:ip,POST /api/admin/users/:ip/avatarGET /api/admin/files,DELETE /api/admin/files/:id
실시간 동기화를 위해 다음 타입의 메시지가 broadcast 됩니다.
type |
의미 |
|---|---|
message |
새 메시지 |
messageEdit |
메시지 본문이 편집됨 (id, text, edited_at) |
delete |
메시지 삭제 |
deleteAttachment |
첨부 단건 삭제 |
reaction |
리액션 토글 |
profilesUpdate |
프로필 변경 |
usersUpdate |
접속자 목록 변경 |
channelsUpdate |
채널/권한 변경 |
activity |
입력 중 / 업로드 중 |
서버 PC의 IP로 접속:
http://<서버-IP>:3000
방화벽에서 해당 포트(기본 3000)를 열어주세요.
- Windows:
ipconfig→ "IPv4 주소" - Mac/Linux:
ifconfig또는ip addr
| 동작 | 키 |
|---|---|
| 메시지 전송 | Enter |
| 줄바꿈 | Shift + Enter |
| 메시지 편집 | hover → ✏ 버튼 |
| 편집 저장 | Enter (편집 중) |
| 편집 취소 | Esc (편집 중) |
| 메시지/첨부 즉시 삭제 | Shift + 클릭 (대상) |
| 임의 키 입력 시 자동 포커스 | 입력창으로 점프 |
Shift를 누르면 화면 하단에 삭제 경고 힌트가 표시됩니다.
concord/
├── server.js # Express + SQLite + WebSocket + API
├── config.json # 권한 / 채널 / 관리자 프로필
├── public/
│ ├── index.html # 채팅 화면
│ ├── admin.html # 관리자 패널 (좌측 세로 탭)
│ ├── bubble.css # Office/Claude 말풍선 스타일 (분리 모듈)
│ ├── bubble.js # 말풍선 렌더러 (단위테스트용)
│ ├── bubble-test.html # /bubble-test.html — 단위테스트 페이지
│ └── favicon.svg
├── uploads/ # 업로드 파일 / 아바타 (자동 생성)
├── void.db # SQLite DB (자동 생성, 레거시 명칭 유지)
└── package.json
- 브랜드를
// VOID→// CONCORD로 변경 (소규모 팀용 Discord/Slack 컨셉) - 사용자가 본인 닉네임/아바타/500자 자기소개 직접 편집
- 채팅의 사용자 이름·아바타 클릭 시 프로필 모달
- 관리자 패널을 좌측 세로 탭 레이아웃으로 개편
- Concord(기본) 테마의 검은 톤 단계화로 영역 가시성 개선
- Office/Claude 테마 말풍선 렌더링 로직 분리 (
bubble.css/bubble.js) + 단위테스트 페이지 - 메시지 편집 기능 추가 (
PATCH /api/messages/:id+ 인라인 에디터)