Angular 17과 Supabase로 구축하는 실시간 채팅 애플리케이션 완벽 가이드
목차
소개
현대 웹 애플리케이션 개발에서 실시간 통신 기능은 필수적인 요소가 되었습니다. 특히 채팅 애플리케이션은 사용자 간의 즉각적인 상호작용을 가능하게 하는 중요한 기능입니다. 이 글에서는 Angular 17과 Supabase를 활용하여 전문적인 실시간 채팅 애플리케이션을 구축하는 방법을 상세히 알아보겠습니다.
"효과적인 실시간 통신은 현대 웹 애플리케이션의 핵심입니다. Angular 17과 Supabase의 조합은 이를 구현하는 최적의 솔루션을 제공합니다."
기술 스택 소개
프론트엔드 기술
- Angular 17
- 최신 제어 흐름 구문
- 독립형 컴포넌트
- 신호(Signals) 시스템
- 향상된 성능과 개발자 경험
- UI 프레임워크
- Bootstrap 5
- 반응형 디자인
- 모던한 UI 컴포넌트
백엔드 기술
- Supabase
- PostgreSQL 데이터베이스
- 실시간 구독 기능
- 강력한 인증 시스템
- 확장 가능한 인프라
- 인증 시스템
- Google OAuth 2.0
- JWT 토큰 관리
- 보안 정책 설정
프로젝트 설정 및 준비
초기 설정
# Angular 프로젝트 생성
ng new chat-application --style=scss --routing=true
# 필요한 의존성 설치
npm install @supabase/supabase-js
npm install bootstrap@5
환경 변수 설정
// environment.ts
export const environment = {
production: false,
supabaseUrl: 'YOUR_SUPABASE_URL',
supabaseKey: 'YOUR_SUPABASE_KEY',
googleClientId: 'YOUR_GOOGLE_CLIENT_ID'
};
핵심 기능 구현
1. 실시간 메시지 처리
실시간 메시지 처리는 Supabase의 구독 시스템을 활용하여 구현합니다.
// message.service.ts
export class MessageService {
private messageSubscription: RealtimeSubscription;
constructor(private supabase: SupabaseService) {
this.setupMessageSubscription();
}
private setupMessageSubscription() {
this.messageSubscription = this.supabase.client
.from('messages')
.on('INSERT', payload => {
this.handleNewMessage(payload.new);
})
.on('DELETE', payload => {
this.handleDeletedMessage(payload.old);
})
.subscribe();
}
async sendMessage(content: string) {
const user = await this.supabase.getCurrentUser();
return this.supabase.client
.from('messages')
.insert([
{
content,
user_id: user.id,
created_at: new Date()
}
]);
}
}
2. 사용자 인증 구현
Google OAuth를 통한 인증 시스템 구현:
// auth.service.ts
export class AuthService {
private authState = signal<User | null>(null);
async signInWithGoogle() {
try {
const { data, error } = await this.supabase.auth.signInWithGoogle();
if (error) throw error;
this.authState.set(data.user);
return data;
} catch (error) {
console.error('Error signing in with Google:', error);
throw error;
}
}
}
3. 메시지 삭제 기능
사용자 권한을 확인하고 메시지를 삭제하는 기능:
async deleteMessage(messageId: string) {
const user = await this.getCurrentUser();
const message = await this.getMessage(messageId);
if (message.user_id !== user.id) {
throw new Error('Unauthorized to delete this message');
}
return this.supabase.client
.from('messages')
.delete()
.match({ id: messageId });
}
보안 및 인증
Row Level Security (RLS) 정책
Supabase에서 데이터 보안을 강화하기 위한 RLS 정책 설정:
-- messages 테이블에 대한 RLS 정책
CREATE POLICY "Users can insert their own messages"
ON messages FOR INSERT
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can view all messages"
ON messages FOR SELECT
TO authenticated
USING (true);
CREATE POLICY "Users can delete their own messages"
ON messages FOR DELETE
USING (auth.uid() = user_id);
데이터베이스 설계
테이블 구조
-- 사용자 테이블
CREATE TABLE users (
id UUID REFERENCES auth.users PRIMARY KEY,
username TEXT UNIQUE,
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 메시지 테이블
CREATE TABLE messages (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
content TEXT NOT NULL,
user_id UUID REFERENCES users(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
UI/UX 구현
채팅 인터페이스 컴포넌트
@Component({
selector: 'app-chat',
template: `
<div class="chat-container">
<div class="message-list" #messageList>
@for (message of messages(); track message.id) {
<div class="message" [class.own-message]="isOwnMessage(message)">
<div class="message-header">
<img [src]="message.user.avatar_url" class="avatar">
<span class="username">{{ message.user.username }}</span>
<span class="timestamp">{{ message.created_at | date:'short' }}</span>
</div>
<div class="message-content">
{{ message.content }}
</div>
@if (isOwnMessage(message)) {
<button class="delete-btn" (click)="deleteMessage(message.id)">
Delete
</button>
}
</div>
}
</div>
<div class="message-input">
<input
type="text"
[(ngModel)]="newMessage"
(keyup.enter)="sendMessage()"
placeholder="Type a message..."
>
<button (click)="sendMessage()">Send</button>
</div>
</div>
`
})
배포 프로세스
Vercel 배포 설정
- GitHub 저장소 연동
- 환경 변수 설정
SUPABASE_URL
SUPABASE_KEY
GOOGLE_CLIENT_ID
- 빌드 설정
- Node.js 버전 지정
- 빌드 명령어 설정
- 도메인 설정
성능 최적화
1. 메시지 페이지네이션
async getMessages(limit: number = 20, offset: number = 0) {
return this.supabase.client
.from('messages')
.select(`
id,
content,
created_at,
user:users(id, username, avatar_url)
`)
.order('created_at', { ascending: false })
.range(offset, offset + limit - 1);
}
2. 이미지 최적화
// 이미지 지연 로딩 디렉티브
@Directive({
selector: '[lazyLoad]'
})
export class LazyLoadDirective implements AfterViewInit {
@Input('lazyLoad') src: string;
constructor(private el: ElementRef) {}
ngAfterViewInit() {
const obs = new IntersectionObserver(entries => {
entries.forEach(({ isIntersecting }) => {
if (isIntersecting) {
this.el.nativeElement.src = this.src;
obs.unobserve(this.el.nativeElement);
}
});
});
obs.observe(this.el.nativeElement);
}
}
결론 및 향후 발전 방향
이 프로젝트를 통해 우리는 다음과 같은 성과를 달성했습니다:
- 안정적인 실시간 통신 구현
- 보안이 강화된 사용자 인증
- 확장 가능한 데이터베이스 구조
- 최적화된 성능
향후 개선 방향:
- 파일 공유 기능 추가
- 그룹 채팅 구현
- 푸시 알림 통합
- 메시지 검색 기능
관련 태그
#Angular #Supabase #WebDevelopment #RealTimeChat #Authentication #TypeScript #WebApp #Programming #Frontend #Database
이 튜토리얼의 전체 소스 코드는 GitHub에서 확인할 수 있으며, 실제 구현 사례를 통해 학습하실 수 있습니다. 추가 질문이나 제안이 있다면 댓글로 남겨주세요.
참고 문헌
- Angular 공식 문서
- Supabase 문서
- Google OAuth 2.0 가이드
- PostgreSQL 문서
반응형
'IT > angular' 카테고리의 다른 글
angular v18이 출시 되었습니다. (Angular v18 is now available) (0) | 2024.09.02 |
---|