Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
b64eae8
chore: 프로젝트 초기 세팅 및 코드 정리 (#1)
bbbang105 Feb 23, 2026
f423621
feat: Supabase Auth + Discord OAuth 인증 전환 (#2)
bbbang105 Feb 23, 2026
74024a5
fix: Vercel outputDirectory 경로 수정 (.next)
bbbang105 Feb 23, 2026
94d99bf
feat: 프로필 이미지 업로드 및 UI 개선 (#3)
bbbang105 Feb 23, 2026
536380a
feat: 스터디원 목록, 프로필 개선, 실명/닉네임 분리 및 소셜 링크 추가 (#4)
bbbang105 Feb 23, 2026
aebcb16
chore: 프레임워크 업그레이드 (Next.js 16 + React 19 + Tailwind v4) (#5)
bbbang105 Feb 24, 2026
a17cf4a
refactor: RSS/스케줄러 마이그레이션 + 큐레이션 시스템 구축 (#6)
bbbang105 Feb 25, 2026
b310024
refactor: 보안 강화, 반응형 개편, 모듈화 및 로그인 수정 (#7)
bbbang105 Feb 26, 2026
4ff7bef
feat: 활동 점수 시스템 + 랭킹 게이미피케이션 구축 (#8)
bbbang105 Feb 26, 2026
b65e986
feat: 글 조회 점수 + RSS 동의 설정 + 수동 글 등록 + 점수 삭제 (#9)
bbbang105 Feb 26, 2026
2288732
feat: 멤버 승인 시스템 + 6단계 상태 관리 구축 (#10)
bbbang105 Feb 27, 2026
f2c6c8c
feat: 커뮤니티 게시판 기능 구현 (게시글 + 댓글 + Tiptap 에디터) (#11)
bbbang105 Feb 27, 2026
8b49e6a
feat: 큐레이션 페이지 리디자인 + 2차 개선 (#12)
bbbang105 Mar 2, 2026
1c680e2
refactor: AI 참조 제거, 문서 최신화, 웹 UI 마무리 + PWA (#13)
bbbang105 Mar 6, 2026
2bebd16
feat: 모바일 하단 탭 바 + 레이아웃 개선 + 문서 최신화 (#14)
bbbang105 Mar 6, 2026
525929c
feat: 봇 슬래시 커맨드 웹 이관 + 회차 관리 + 웹 기능 보강 (#15) (#15)
bbbang105 Mar 6, 2026
0791b47
docs: 온보딩 문서 추가
bbbang105 Mar 8, 2026
ced5410
feat: 디스코드 채널 세팅 + 회차 알림 공지 채널 분리 (#16)
bbbang105 Mar 8, 2026
cb2ab18
feat: 공지 배너 + 풀투리프레시 + UX 개선 (#17)
bbbang105 Mar 8, 2026
7567733
fix: Safari PWA 다이얼로그 스크롤 수정
bbbang105 Mar 8, 2026
fb291af
Merge pull request #18 from bbbang105/fix/safari-dialog-scroll
bbbang105 Mar 8, 2026
2189a38
feat: 웹 API 표준화 + 보안 강화 + 에러 처리 개선
bbbang105 Mar 8, 2026
5fae290
Merge pull request #19 from bbbang105/feat/web-api-standardize-security
bbbang105 Mar 8, 2026
966294a
feat: 랜딩 페이지 리디자인 + 커스텀 로고/파비콘 (#20)
bbbang105 Mar 8, 2026
8bd9b64
style: 서비스명 '큐스팅 4th'로 전체 통일
bbbang105 Mar 8, 2026
305eadc
feat: Discord.js 스킬 모듈 추가
choihooo Mar 8, 2026
2d94e03
refactor: 프로젝트 철학에 맞지 않는 스킬 제거
choihooo Mar 9, 2026
d19afad
refactor: 스킬 시스템 정리 완료 + 프로젝트 참고용 스킬 추가
choihooo Mar 9, 2026
d9bf23e
Merge pull request #21 from bbbang105/feat/bot-skills
choihooo Mar 9, 2026
8d45abd
feat: 주간 랭킹 자동 발송 기능 구현 (#22)
choihooo Mar 9, 2026
0dc36cf
test: 봇 기능 전체 테스트 및 버그 수정 (#23)
choihooo Mar 10, 2026
b1d5463
feat: 벌금 납부 확인을 버튼 기반으로 변경 (#24)
choihooo Mar 10, 2026
a110ffc
feat: RSS 수집 시 출석 상태 업데이트 및 결석/지각 벌금 자동 부과 (#25)
choihooo Mar 10, 2026
f582093
fix: 회차 스케줄러 버그 수정 (P0 #5, #6, #7) (#26)
choihooo Mar 10, 2026
08623e9
fix: Fine Reminder 3일 간격 로직 수정 (P1 #10) (#27)
choihooo Mar 10, 2026
c81c093
feat: 큐레이션 데이터 품질 개선 (description, thumbnailUrl 추출) (#28)
choihooo Mar 11, 2026
db6e003
test: P2 #15 핸들러 및 스케줄러 테스트 커버리지 추가 (#29)
choihooo Mar 11, 2026
3170765
feat(a11y): P1 접근성 이슈 수정 (WCAG 2.1 AA 준수) (#32)
choihooo Mar 11, 2026
e499e0f
fix: P0 웹 접근성 이슈 수정 (색상 대비, 폼 에러, 키보드 내비게이션) (#30)
choihooo Mar 11, 2026
59908cd
fix(a11y): P2 접근성 이슈 개선 (Landmark, Skip-to-Content, ARIA Labels) (#31)
choihooo Mar 11, 2026
157e4dd
fix: 웹 QA 및 관리자 안정성 개선 (#33)
choihooo Mar 11, 2026
a689972
Improve web responsive UI and interaction quality (#34)
choihooo Mar 12, 2026
bccd0ab
feat: 포스트 OG 썸네일, 인기순 탭, 무한 스크롤 및 QA 개선 (#35)
bbbang105 Mar 12, 2026
c47f25f
feat: 봇 Docker/ECR 배포 파이프라인 구축 및 Railway 제거 (#36)
bbbang105 Mar 12, 2026
bb840c6
feat(bot): pino 구조화된 로깅 추가 (#38)
choihooo Mar 13, 2026
32ed243
feat(web): 봇 수동 제어 기능 추가 (#41)
choihooo Mar 13, 2026
3a719a3
feat(web): Sentry 에러 모니터링 연동 (#42)
bbbang105 Mar 13, 2026
f03ce28
feat(bot): Sentry 에러 모니터링 연동 (Discord error-webhook 대체) (#43)
bbbang105 Mar 13, 2026
94787c0
fix: 봇 로직 P0-P2 수정, API 인증, 네비게이션 및 벌금 알림 개선 (#44)
bbbang105 Mar 13, 2026
e610640
fix(bot): 주간 랭킹 Date 객체 → ISO 문자열로 변환 (Drizzle sql 호환)
bbbang105 Mar 13, 2026
93ff129
fix(web): middleware.ts 복원 (Next.js 16 업그레이드 시 삭제됨)
bbbang105 Mar 13, 2026
096e1f5
fix(web): 관리자 권한 다이얼로그 복원 및 미들웨어 수정 (#45)
bbbang105 Mar 13, 2026
e4317fb
feat(web): 게시판 날짜 투표 및 투표 변경 기능 구현 (#46)
choihooo Mar 16, 2026
c302ac6
feat: 활동 점수 디스코드→웹 전환 + 대시보드 점수 카드 (#47)
bbbang105 Mar 16, 2026
4955c57
feat(web): PWA 푸시 알림 시스템 구현
choihooo Mar 16, 2026
6343c33
Revert "feat(web): PWA 푸시 알림 시스템 구현"
choihooo Mar 16, 2026
19c9018
feat: 봇 알림 개선 + 포스트 검색/필터 + 보안 강화 (#50)
bbbang105 Mar 16, 2026
4560bd7
feat(web): PWA 푸시 알림 시스템 구현 (#49)
choihooo Mar 16, 2026
c78ec01
fix(web): Firebase SW API 키 하드코딩 제거 → 환경변수 동적 주입 (#51)
bbbang105 Mar 17, 2026
6c09d52
fix(web): Firebase SW 환경변수 런타임 주입으로 변경 (#52)
bbbang105 Mar 17, 2026
95934c8
fix(web): 푸시 알림 after() 전환 + SW 정리 + 알림 로직 개선 (#53)
bbbang105 Mar 17, 2026
4f62ed2
feat: QA3 — 포스트 비밀댓글 + 대시보드 출석 칩 + 랜딩 리뉴얼 + UX 개선 (#54)
bbbang105 Mar 17, 2026
ef8b9a3
feat(web): OG 이미지 동적 생성 + 댓글 placeholder 유니코드 수정 (#55)
bbbang105 Mar 18, 2026
62e0e5b
refactor(web): 게시판 PC/모바일 카드 레이아웃 통일 (#56)
bbbang105 Mar 18, 2026
87cc6d6
feat(web): 스터디원 OB/휴면 표시 + 포스트 삭제 기능 + 휴면 제한 해제 (#57)
bbbang105 Mar 18, 2026
99751cc
feat(web): 신규 가입 관리자 채널 알림 + Tiptap 백틱 렌더링 수정 (#58)
bbbang105 Mar 18, 2026
52e5023
style(web): 공지 배너를 사이드바 밖에서 콘텐츠 영역 안으로 이동 (#59)
bbbang105 Mar 18, 2026
a97efac
feat(web): 프로필 수정에서 블로그 URL 편집 + RSS 재감지 기능 추가 (#60) (#60)
bbbang105 Mar 18, 2026
76c40b6
chore(bot): RSS 수집 시작일을 3/23으로 변경 (#61)
bbbang105 Mar 18, 2026
40336fa
feat(web/bot): 대시보드 UX, 이미지 첨부, 포스트 관리, 랭킹/알림 개선 (#62)
bbbang105 Mar 22, 2026
1529c84
fix(bot): RSS 새 글 알림에 딥링크 버튼 추가 (웹 수동등록과 동일 형태) (#63)
bbbang105 Mar 22, 2026
4abbaff
feat(web): 게시글당 투표 여러 개 생성 가능하도록 제한 해제 (#64) (#64)
bbbang105 Mar 23, 2026
04213e4
feat(web): 투표 전 결과 숨김 처리 (#65) (#65)
bbbang105 Mar 23, 2026
73df652
feat(web/bot): 새 글 등록 시 FCM 푸시 알림 발송 (#66) (#66)
bbbang105 Mar 23, 2026
dc17ec9
chore(bot): RSS 새 글 처리 로깅 개선 (#67)
bbbang105 Mar 23, 2026
8b01b65
fix(web): 게시판 공지글 작성 시 활동 점수 부여 제외
bbbang105 Mar 25, 2026
60f914b
feat: 랭킹 관리자 제외 + 프로필 페이지 UX 개선 (#68)
bbbang105 Mar 25, 2026
efd31d7
fix(web): iOS PWA 상태바 콘텐츠 침범 방지
bbbang105 Mar 26, 2026
603a5d0
fix(bot): RSS 폴링 최적화 + pino Error 직렬화 수정 (#69)
bbbang105 Mar 26, 2026
54da106
feat: 투표 미참여 DM + 마감 리마인더 + 게시판 UX 개선 (#70)
bbbang105 Mar 29, 2026
c53feaf
feat: 게시판/포스트 이모지 리액션 기능 추가 (#71)
bbbang105 Apr 1, 2026
674e8e0
style(web): reduce post card footer reaction gap spacing
bbbang105 Apr 1, 2026
3ca3148
feat: D-Day 수정 + 비밀답글 가시성 + 랭킹 포부 2줄 + Discord 알림 로그 시스템 (#72)
bbbang105 Apr 3, 2026
ccdd6e4
fix: D-Day 라벨 off-by-one 수정 + 주간랭킹 수동부여 점수 포함
bbbang105 Apr 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 16 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--headless"
]
}
},
"permissions": {
"allow": [
"Bash"
]
}
}
15 changes: 15 additions & 0 deletions .claude/skills/events-handler/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "events-handler",
"description": "Discord.js 이벤트 처리 참고 (현재 프로젝트 패턴 기반)",
"version": "1.0.0",
"author": "Blog Study",
"tags": ["discord", "events", "handlers", "reference"],
"keywords": ["discord.js", "events", "handlers", "patterns", "examples"],
"category": "reference",
"patterns": [
"event-handler-registration",
"error-handling",
"message-handling",
"reaction-handling"
]
}
149 changes: 149 additions & 0 deletions .claude/skills/events-handler/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Events Handler Reference

Discord.js v14 이벤트 처리 예시 모음 - 현재 프로젝트 패턴 기반

## 사용 방법
새로운 이벤트 핸들러 구현 시 참고

## 기본 패턴 (현재 프로젝트 방식)

```typescript
import type { Client, Message } from 'discord.js';
import { Events } from 'discord.js';

/**
* 커스텀 이벤트 핸들러 등록
*/
export function setupCustomHandler(client: Client): void {
// 메시지 생성 이벤트
client.on(Events.MessageCreate, async (message: Message) => {
try {
// 봇 메시지 무시
if (message.author.bot) return;

// DM 무시
if (!message.guild) return;

// 처리 로직
console.log(`[CustomHandler] Message from ${message.author.id}`);
} catch (error) {
console.error('[CustomHandler] Error:', error);
}
});

console.log('✅ Custom handler registered');
}
```

## 주요 이벤트 타입

### 1. 봇 시작 (ClientReady)
```typescript
client.once(Events.ClientReady, (readyClient) => {
console.log(`✅ Bot logged in as ${readyClient.user.tag}`);
console.log(`📊 Serving ${readyClient.guilds.cache.size} guild(s)`);
});
```

### 2. 메시지 생성 (MessageCreate)
```typescript
client.on(Events.MessageCreate, async (message: Message) => {
if (message.author.bot) return;
if (!message.guild) return;

// 메시지 처리
});
```

### 3. 리액션 추가 (MessageReactionAdd)
```typescript
client.on(Events.MessageReactionAdd, async (reaction, user) => {
if (user.bot) return;

// partial인 경우 fetch
if (reaction.partial) {
await reaction.fetch();
}

// 리액션 처리
});
```

### 4. 리액션 제거 (MessageReactionRemove)
```typescript
client.on(Events.MessageReactionRemove, async (reaction, user) => {
if (user.bot) return;

// 리액션 제거 처리
});
```

### 5. 음성 상태 변경 (VoiceStateUpdate)
```typescript
client.on(Events.VoiceStateUpdate, (oldState, newState) => {
// 음성 채널 입장/퇴장 처리
});
```

### 6. 멤버 입장 (GuildMemberAdd)
```typescript
client.on(Events.GuildMemberAdd, (member) => {
// 새 멤버 환영
});
```

### 7. 에러 처리 (Error)
```typescript
client.on(Events.Error, (error) => {
console.error('❌ Discord client error:', error);
});
```

## 등록 방법 (index.ts)

```typescript
import { createBotClient, setupEventHandlers } from './bot';
import { setupActivityHandler } from './handlers/activity-handler';
import { setupCustomHandler } from './handlers/custom-handler';

async function main(): Promise<void> {
const client = createBotClient();

// 기본 이벤트 핸들러
setupEventHandlers(client);

// 활동 점수 핸들러
setupActivityHandler(client);

// 커스텀 핸들러
setupCustomHandler(client);

await startBot(client, env.DISCORD_TOKEN);
}
```

## 에러 처리 패턴

모든 이벤트 핸들러는 try-catch로 감싸야 합니다:

```typescript
client.on(Events.SomeEvent, async (...args) => {
try {
// 이벤트 처리 로직
} catch (error) {
console.error('[HandlerName] Error:', error);
// 필요시 로깅 또는 알림
}
});
```

## 프로젝트에서 사용 중인 핸들러

- `setupEventHandlers()` - 기본 이벤트 (ready, error, warn)
- `setupActivityHandler()` - 활동 점수 (message, reaction)
- `setupDMHandler()` - DM 처리 (벌금 납부 확인)

## 참고

- Discord.js v14.25.1 Events: https://discord.js.org/docs/packages/discord.js/14.25.1/Classes/Client
- 현재 프로젝트: `packages/bot/src/handlers/` 참고
15 changes: 15 additions & 0 deletions .claude/skills/scheduler-reference/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "scheduler-reference",
"description": "pg-boss 스케줄러 구현 참고 (현재 프로젝트 패턴 기반)",
"version": "1.0.0",
"author": "Blog Study",
"tags": ["scheduler", "pg-boss", "cron", "jobs", "reference"],
"keywords": ["pg-boss", "scheduler", "cron", "worker", "singleton"],
"category": "reference",
"patterns": [
"singleton-pattern",
"isRunning-flag",
"cron-scheduling",
"worker-registration"
]
}
183 changes: 183 additions & 0 deletions .claude/skills/scheduler-reference/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Scheduler Reference

pg-boss 기반 스케줄러 구현 참고 - 현재 프로젝트 패턴 기반

## 사용 방법
새로운 스케줄러 구현 시 참고

## 기본 패턴

```typescript
/**
* 스케줄러 클래스 템플릿
*/
export class MyScheduler {
private isRunning = false;

/**
* 실행 중인지 확인
*/
isChecking(): boolean {
return this.isRunning;
}

/**
* 작업 실행
*/
async run(): Promise<Result> {
if (this.isRunning) {
console.log('[MyScheduler] Already in progress, skipping');
return {
timestamp: new Date(),
errors: ['Already in progress'],
};
}

this.isRunning = true;

try {
// 작업 로직 구현
console.log('[MyScheduler] Running...');

return {
timestamp: new Date(),
success: true,
};
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error);
console.error(`[MyScheduler] Error: ${errorMsg}`);

return {
timestamp: new Date(),
errors: [errorMsg],
};
} finally {
this.isRunning = false;
}
}
}

// Singleton 인스턴스
let instance: MyScheduler | null = null;

export function getMyScheduler(): MyScheduler {
if (!instance) {
instance = new MyScheduler();
}
return instance;
}

export function resetMyScheduler(): void {
instance = null;
}
```

## scheduler-registry.ts에 등록

```typescript
import { getMyScheduler } from './schedulers/my-scheduler';

export async function registerAllJobs(boss: PgBoss, client: Client): Promise<void> {
const myScheduler = getMyScheduler();

// 1. Cron 잡 등록 (매일 09:00)
await boss.schedule('my-job', '0 9 * * *');
console.log(' 📅 Scheduled: my-job (0 9 * * *)');

// 2. 워커 등록
await boss.work('my-job', { batchSize: 1 }, async () => {
await myScheduler.run();
});

console.log(`✅ All jobs registered`);
}
```

## Discord 클라이언트가 필요한 경우

```typescript
export class MyScheduler {
private client: Client | null = null;

setClient(client: Client): void {
this.client = client;
}

async run(): Promise<Result> {
if (!this.client) {
console.error('[MyScheduler] Discord client not set');
return {
timestamp: new Date(),
errors: ['Discord client not set'],
};
}

// client 사용
const channel = await this.client.channels.fetch('CHANNEL_ID');
// ...
}
}

// 등록 시
const myScheduler = getMyScheduler();
myScheduler.setClient(client);
```

## Cron 표현식 예시

| 표현식 | 설명 |
|---------|------|
| `*/5 * * * *` | 5분마다 |
| `0 9 * * *` | 매일 09:00 |
| `0 0 * * 2` | 매주 화요일 00:00 |
| `0 23 * * *` | 매일 23:00 (KST 익일 08:00) |
| `0 0 1 * *` | 매월 1일 00:00 |

## pg-boss 설정

```typescript
import { PgBoss } from 'pg-boss';

// 시작
const boss = new PgBoss(connectionString);
boss.on('error', (error: Error) => console.error('[pg-boss] Error:', error));
await boss.start();

// Cron 잡 등록
await boss.schedule('job-name', 'cron-expression');

// 워커 등록
await boss.work('job-name', { batchSize: 1 }, async () => {
// 작업 실행
});

// 종료
await boss.stop({ graceful: true, timeout: 30000 });
```

## 결과 인터페이스

```typescript
interface Result {
timestamp: Date;
success?: boolean;
errors: string[];
// 기타 필요한 필드
}
```

## 프로젝트 예시 참고

- `rss-poller.ts` - RSS 폴링 (5분)
- `attendance-checker.ts` - 출석 체크 (주간)
- `fine-reminder.ts` - 벌금 리마인더 (일일)
- `round-reporter.ts` - 회차 리포트 (주간)
- `curation-crawler.ts` - 큐레이션 크롤링 (일일)

## 주의사항

1. **동시 실행 방지**: `isRunning` 플래그로 중복 실행 방지
2. **에러 처리**: 모든 에러를 catch하고 결과 객체에 포함
3. **로그**: 진행 상황을 console.log로 출력
4. **Singleton**: 한 인스턴스만 사용 (`getXxx()` 함수)
5. **테스트용 reset**: `resetXxx()` 함수 제공
16 changes: 16 additions & 0 deletions .claude/skills/service-pattern/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "service-pattern",
"description": "DB 서비스 계층 구현 참고 (현재 프로젝트 패턴 기반)",
"version": "1.0.0",
"author": "Blog Study",
"tags": ["service", "database", "drizzle", "orm", "reference"],
"keywords": ["drizzle-orm", "service-layer", "crud", "singleton", "repository"],
"category": "reference",
"patterns": [
"singleton-pattern",
"error-handling",
"crud-operations",
"complex-queries",
"transactions"
]
}
Loading
Loading